You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
`CodecMapper` is a schema-first serialization library for F# focused on explicit wire contracts, symmetric encode/decode behavior, and portability to Native AOT and Fable-style targets.
17
+
CodecMapper is a schema-first serialization library for F# focused on explicit wire contracts,
18
+
symmetric encode/decode behavior, and portability to Native AOT and Fable-style targets.
18
19
19
-
It is for cases where serializer attributes and implicit conventions stop being helpful. You define one schema that mirrors the wire shape, then compile it into reusable codecs.
20
+
It's for cases where serializer attributes and implicit conventions stop being helpful.
21
+
You define one schema that mirrors the wire shape, then compile it into reusable codecs.
20
22
21
23
## Why the schema feels different
22
24
23
25
```fsharp
24
26
open CodecMapper
27
+
open CodecMapper.Schema
25
28
26
29
type Address = { Street: string; City: string }
27
30
let makeAddress street city = { Street = street; City = city }
@@ -30,19 +33,19 @@ type Person = { Id: int; Name: string; Home: Address }
30
33
let makePerson id name home = { Id = id; Name = name; Home = home }
31
34
32
35
let addressSchema =
33
-
Schema.define<Address>
34
-
|> Schema.construct makeAddress
35
-
|> Schema.field "street" _.Street
36
-
|> Schema.field "city" _.City
37
-
|> Schema.build
36
+
define<Address>
37
+
|> construct makeAddress
38
+
|> field "street" _.Street
39
+
|> field "city" _.City
40
+
|> build
38
41
39
42
let personSchema =
40
-
Schema.define<Person>
41
-
|> Schema.construct makePerson
42
-
|> Schema.field "id" _.Id
43
-
|> Schema.field "name" _.Name
44
-
|> Schema.fieldWith "home" _.Home addressSchema
45
-
|> Schema.build
43
+
define<Person>
44
+
|> construct makePerson
45
+
|> field "id" _.Id
46
+
|> field "name" _.Name
47
+
|> fieldWith "home" _.Home addressSchema
48
+
|> build
46
49
47
50
let codec = Json.compile personSchema
48
51
let person =
@@ -142,6 +145,8 @@ One of the main benefits over convention-based serializers is that model evoluti
142
145
If your domain gets richer but the wire contract does not need to change yet, keep the same wire shape and refine it:
let makePersonV2 id name email = { Id = id; Name = name; Email = email }
182
189
183
190
let personV2Schema =
184
-
Schema.define<PersonV2>
185
-
|> Schema.construct makePersonV2
186
-
|> Schema.field "id" _.Id
187
-
|> Schema.field "name" _.Name
188
-
|> Schema.field "email" _.Email
189
-
|> Schema.build
191
+
define<PersonV2>
192
+
|> construct makePersonV2
193
+
|> field "id" _.Id
194
+
|> field "name" _.Name
195
+
|> field "email" _.Email
196
+
|> build
190
197
```
191
198
192
199
That does not silently "pick up" the new field just because the record changed. You add it deliberately to the schema, so the contract review point is explicit.
@@ -227,7 +234,8 @@ When benchmark numbers move, profile before changing the runtime. The repo now i
227
234
## Docs
228
235
229
236
- Start with [Getting started](docs/GETTING_STARTED.md).
230
-
- Copy from [How to model common contract patterns](docs/HOW_TO_MODEL_COMMON_CONTRACT_PATTERNS.md).
237
+
- Use the [contract pattern index](docs/HOW_TO_MODEL_COMMON_CONTRACT_PATTERNS.md) when you need a quick jump page.
238
+
- Copy from [How to model a basic record](docs/HOW_TO_MODEL_A_BASIC_RECORD.md), [how to model a nested record](docs/HOW_TO_MODEL_A_NESTED_RECORD.md), [how to model a validated wrapper](docs/HOW_TO_MODEL_A_VALIDATED_WRAPPER.md), or [how to model a versioned contract](docs/HOW_TO_MODEL_A_VERSIONED_CONTRACT.md).
231
239
- Use [Configuration contracts guide](docs/CONFIG_CONTRACTS.md) for versioned config shapes.
232
240
- Use [How to export JSON Schema](docs/HOW_TO_EXPORT_JSON_SCHEMA.md) and [JSON Schema support reference](docs/JSON_SCHEMA_SUPPORT.md) for schema interchange.
233
241
- Use [How to import existing C# contracts](docs/HOW_TO_IMPORT_CSHARP_CONTRACTS.md) for the bridge/facade story.
That keeps the default local to the contract instead of smuggling it through serializer settings or post-deserialize mutation.
@@ -94,22 +96,24 @@ That keeps the default local to the contract instead of smuggling it through ser
94
96
Some config boundaries treat an explicit `null` or an explicit empty collection as "use the contract default" rather than as a distinct payload state. Keep that normalization local to the field too:
0 commit comments