@zio.dev/zio-blocks 0.0.33 → 0.0.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/guides/compile-time-resource-safety-with-scope.md +16 -17
- package/guides/getting-started-with-mux.md +1507 -0
- package/guides/query-dsl-extending.md +161 -102
- package/guides/query-dsl-fluent-builder.md +217 -157
- package/guides/query-dsl-reified-optics.md +12 -10
- package/guides/query-dsl-sql.md +246 -165
- package/guides/telemetry-guide.md +1069 -0
- package/guides/zio-schema-migration.md +29 -22
- package/index.md +292 -50
- package/package.json +1 -1
- package/plans/config-follow-up-prs.md +188 -0
- package/plans/config-pr-assessment-roadmap.md +310 -0
- package/reference/MuxDataFlow.jsx +250 -0
- package/reference/async.md +651 -0
- package/reference/chunk.md +3533 -308
- package/reference/codegen/case-class.md +436 -0
- package/reference/codegen/emitter-config.md +383 -0
- package/reference/codegen/examples.md +664 -0
- package/reference/codegen/field.md +316 -0
- package/reference/codegen/index.md +317 -0
- package/reference/codegen/scala-emitter.md +392 -0
- package/reference/codegen/scala-file.md +276 -0
- package/reference/codegen/sealed-trait.md +408 -0
- package/reference/codegen/type-definition.md +340 -0
- package/reference/codegen/type-ref.md +201 -0
- package/reference/combinators.md +347 -117
- package/reference/config.md +158 -0
- package/reference/context.md +4 -4
- package/reference/datastar.md +346 -0
- package/reference/docs.md +1461 -345
- package/reference/endpoint/auth-type.md +146 -0
- package/reference/endpoint/endpoint.md +297 -0
- package/reference/endpoint/http-codec.md +249 -0
- package/reference/endpoint/index.md +825 -0
- package/reference/endpoint/path-codec.md +237 -0
- package/reference/endpoint/route-pattern.md +196 -0
- package/reference/endpoint/route-tree.md +111 -0
- package/reference/endpoint/segment-codec.md +212 -0
- package/reference/html.md +1120 -0
- package/reference/htmx/attribute-values.md +359 -0
- package/reference/htmx/hx-encoding.md +111 -0
- package/reference/htmx/hx-params.md +204 -0
- package/reference/htmx/hx-swap.md +276 -0
- package/reference/htmx/hx-sync.md +251 -0
- package/reference/htmx/hx-target.md +314 -0
- package/reference/htmx/hx-trigger.md +457 -0
- package/reference/htmx/hx-url-update.md +239 -0
- package/reference/htmx/index.md +855 -0
- package/reference/http-model/index.md +47 -0
- package/reference/http-model/model.md +1481 -0
- package/reference/http-model/schema.md +747 -0
- package/reference/maybe.md +826 -0
- package/reference/media-type.md +2 -2
- package/reference/mux.mdx +823 -0
- package/reference/openapi.md +1351 -0
- package/reference/resource-management/defer-handle.md +1 -1
- package/reference/resource-management/resource.md +31 -2
- package/reference/resource-management/scope.md +28 -12
- package/reference/resource-management/wire.md +3 -7
- package/reference/ringbuffer/MpmcDiagram.jsx +717 -0
- package/reference/ringbuffer/MpscDiagram.jsx +618 -0
- package/reference/ringbuffer/SpmcDiagram.jsx +680 -0
- package/reference/ringbuffer/SpscDiagram.jsx +677 -0
- package/reference/ringbuffer/advanced.mdx +109 -0
- package/reference/ringbuffer/index.mdx +145 -0
- package/reference/ringbuffer/mpmc.mdx +151 -0
- package/reference/ringbuffer/mpsc.mdx +132 -0
- package/reference/ringbuffer/spmc.mdx +108 -0
- package/reference/ringbuffer/spsc.mdx +344 -0
- package/reference/{allows.md → schema/allows.md} +4 -4
- package/reference/{binding-resolver.md → schema/binding-resolver.md} +1 -1
- package/reference/{binding.md → schema/binding.md} +2 -3
- package/reference/schema/built-in-codecs/avro.md +451 -0
- package/reference/schema/built-in-codecs/bson.md +480 -0
- package/reference/schema/built-in-codecs/csv.md +564 -0
- package/reference/schema/built-in-codecs/index.md +77 -0
- package/reference/schema/built-in-codecs/json/index.md +295 -0
- package/reference/schema/built-in-codecs/json/json-config.md +217 -0
- package/reference/{json-patch.md → schema/built-in-codecs/json/json-patch.md} +5 -5
- package/reference/{json-schema.md → schema/built-in-codecs/json/json-schema.md} +14 -47
- package/reference/schema/built-in-codecs/json/json-selection.md +322 -0
- package/reference/{json.md → schema/built-in-codecs/json/json.md} +32 -64
- package/reference/schema/built-in-codecs/messagepack.md +508 -0
- package/reference/schema/built-in-codecs/thrift.md +433 -0
- package/reference/schema/built-in-codecs/toon.md +1078 -0
- package/reference/{xml.md → schema/built-in-codecs/xml.md} +13 -9
- package/reference/schema/built-in-codecs/yaml.md +552 -0
- package/reference/{codec.md → schema/codec.md} +10 -10
- package/reference/{dynamic-optic.md → schema/dynamic-optic.md} +151 -5
- package/reference/{dynamic-schema.md → schema/dynamic-schema.md} +8 -8
- package/reference/schema/format.md +92 -0
- package/reference/schema/index.md +50 -0
- package/reference/schema/migration.md +297 -0
- package/reference/{modifier.md → schema/modifier.md} +58 -7
- package/reference/{optics.md → schema/optics.md} +2 -2
- package/reference/{patch.md → schema/patch.md} +1 -1
- package/{path-interpolator.md → reference/schema/path-interpolator.md} +165 -72
- package/reference/{schema-evolution → schema/schema-evolution}/as.md +8 -8
- package/reference/{schema-evolution → schema/schema-evolution}/index.md +2 -2
- package/reference/{schema-evolution → schema/schema-evolution}/into.md +8 -8
- package/reference/{schema-expr.md → schema/schema-expr.md} +110 -175
- package/reference/{schema.md → schema/schema.md} +12 -0
- package/reference/{structural-types.md → schema/structural-types.md} +1 -1
- package/reference/{type-class-derivation.md → schema/type-class-derivation.md} +63 -1
- package/reference/smithy.md +533 -0
- package/reference/sql/db-codec-deriver.md +71 -0
- package/reference/sql/db-codec.md +687 -0
- package/reference/sql/db-con.md +271 -0
- package/reference/sql/db-connection.md +153 -0
- package/reference/sql/db-param-writer.md +77 -0
- package/reference/sql/db-param.md +66 -0
- package/reference/sql/db-result-reader.md +146 -0
- package/reference/sql/db-tx.md +82 -0
- package/reference/sql/db-value.md +41 -0
- package/reference/sql/ddl.md +85 -0
- package/reference/sql/frag.md +254 -0
- package/reference/sql/index.md +341 -0
- package/reference/sql/repo.md +600 -0
- package/reference/sql/sql-dialect.md +73 -0
- package/reference/sql/sql-logger.md +62 -0
- package/reference/sql/sql-name-mapper.md +70 -0
- package/reference/sql/table-metadata.md +134 -0
- package/reference/sql/table.md +448 -0
- package/reference/sql/transactor-zio.md +399 -0
- package/reference/sql/transactor.md +353 -0
- package/reference/sql-zio.md +112 -0
- package/reference/streams/concurrent-operators.md +106 -0
- package/reference/streams/index.md +653 -0
- package/reference/streams/pipeline.md +718 -0
- package/reference/streams/reader.md +1284 -0
- package/reference/streams/scala-2-compatibility.md +55 -0
- package/reference/streams/sink.md +1426 -0
- package/reference/streams/stream.md +2526 -0
- package/reference/streams/writer.md +1045 -0
- package/reference/streams/zero-boxing.md +275 -0
- package/reference/telemetry.md +693 -0
- package/reference/typeid.md +5 -19
- package/sidebars.js +238 -43
- package/reference/formats.md +0 -694
- package/reference/http-model.md +0 -1716
- package/reference/streams.md +0 -989
- package/ringbuffer.md +0 -249
- /package/reference/{json-differ.md → schema/built-in-codecs/json/json-differ.md} +0 -0
- /package/reference/{dynamic-value.md → schema/dynamic-value.md} +0 -0
- /package/reference/{lazy.md → schema/lazy.md} +0 -0
- /package/reference/{reflect.md → schema/reflect.md} +0 -0
- /package/reference/{registers.md → schema/registers.md} +0 -0
- /package/reference/{schema-error.md → schema/schema-error.md} +0 -0
- /package/reference/{syntax.md → schema/syntax.md} +0 -0
- /package/reference/{validation.md → schema/validation.md} +0 -0
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: index
|
|
3
|
+
title: "JSON Codec"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
The JSON codec module provides complete, type-safe support for working with JSON data in ZIO Blocks. It includes an ADT for representing JSON values, a fluent navigation API (`JsonSelection`), configurable encoding/decoding (`JsonCodec` with `WriterConfig`/`ReaderConfig`), composable patches for transformations, a diff algorithm for computing minimal changes, and full JSON Schema 2020-12 support for validation and code generation.
|
|
7
|
+
|
|
8
|
+
**Core types:** `Json`, `JsonCodec`, `JsonSelection`, `JsonPatch`, `JsonDiffer`, `JsonSchema`, `JsonType`.
|
|
9
|
+
|
|
10
|
+
```scala
|
|
11
|
+
import zio.blocks.schema._
|
|
12
|
+
import zio.blocks.schema.json._
|
|
13
|
+
|
|
14
|
+
// Represent any JSON value
|
|
15
|
+
val person = Json.Object("name" -> Json.String("Alice"), "age" -> Json.Number(30))
|
|
16
|
+
|
|
17
|
+
// Navigate with fluent API
|
|
18
|
+
val age = person.get("age") // JsonSelection
|
|
19
|
+
|
|
20
|
+
// Encode to string
|
|
21
|
+
val encoded = person.print(WriterConfig)
|
|
22
|
+
|
|
23
|
+
// Compute minimal patches
|
|
24
|
+
val updated = person.set(p".age", Json.Number(31))
|
|
25
|
+
val patch = JsonPatch.diff(person, updated)
|
|
26
|
+
|
|
27
|
+
// Validate against a schema
|
|
28
|
+
case class Person(name: String, age: Int)
|
|
29
|
+
object Person { implicit val schema: Schema[Person] = Schema.derived }
|
|
30
|
+
val schema: JsonSchema = Schema[Person].toJsonSchema
|
|
31
|
+
schema.conforms(person) // true
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
The JSON codec is included in the ZIO Blocks Schema module. Add it to your `build.sbt`:
|
|
37
|
+
|
|
38
|
+
```scala
|
|
39
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-schema" % "0.0.51"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
For Scala.js projects, use `%%%` instead:
|
|
43
|
+
|
|
44
|
+
```scala
|
|
45
|
+
libraryDependencies += "dev.zio" %%% "zio-blocks-schema" % "0.0.51"
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**Supported Scala versions:** 2.13.x and 3.x
|
|
49
|
+
|
|
50
|
+
The JSON codec is fully integrated into ZIO Blocks Schema and provides complete type-safe JSON support with no external dependencies beyond core ZIO libraries.
|
|
51
|
+
|
|
52
|
+
## How They Work Together
|
|
53
|
+
|
|
54
|
+
The JSON module workflow moves through representation, navigation, encoding/decoding, diffing, patching, and validation:
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
Parse/Create JSON
|
|
58
|
+
↓
|
|
59
|
+
Json (ADT: Object, Array, String, Number, Boolean, Null)
|
|
60
|
+
├─ Navigate with JsonSelection (fluent query API)
|
|
61
|
+
├─ Understand with JsonType (type information)
|
|
62
|
+
└─ Transform via JsonPatch (composable operations)
|
|
63
|
+
↓
|
|
64
|
+
JsonCodec (encode/decode)
|
|
65
|
+
├─ WriterConfig (indent, escaping, order)
|
|
66
|
+
├─ ReaderConfig (strict validation, number handling)
|
|
67
|
+
└─ MergeStrategy (field conflict resolution)
|
|
68
|
+
↓
|
|
69
|
+
JsonDiffer (diff algorithm)
|
|
70
|
+
└─ Produces JsonPatch (minimal changes)
|
|
71
|
+
↓
|
|
72
|
+
JsonPatch.apply (transform JSON)
|
|
73
|
+
↓
|
|
74
|
+
JsonSchema (validate or generate)
|
|
75
|
+
├─ Derive from Scala types
|
|
76
|
+
├─ Validate JSON conformance
|
|
77
|
+
└─ Generate documentation
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Type Relationships:**
|
|
81
|
+
- `Json` is the central value type; all operations start with or produce it
|
|
82
|
+
- `JsonSelection` provides fluent navigation through nested `Json` structures
|
|
83
|
+
- `JsonCodec` bridges Scala types ↔ `Json` with configurable `WriterConfig` and `ReaderConfig`
|
|
84
|
+
- `JsonDiffer` computes `JsonPatch` by comparing two `Json` values
|
|
85
|
+
- `JsonPatch` transforms `Json` values with composable operations
|
|
86
|
+
- `JsonSchema` validates `Json` conformance and derives from Scala `Schema` types
|
|
87
|
+
- `JsonType` provides type information for `Json` values at runtime
|
|
88
|
+
|
|
89
|
+
## Common Patterns
|
|
90
|
+
|
|
91
|
+
### Pattern 1: Parse, Transform, Serialize
|
|
92
|
+
|
|
93
|
+
Read JSON, modify it, and write it back:
|
|
94
|
+
|
|
95
|
+
```scala
|
|
96
|
+
import zio.blocks.schema._
|
|
97
|
+
import zio.blocks.schema.json.{Json, WriterConfig}
|
|
98
|
+
|
|
99
|
+
val jsonString = """{"name": "Alice", "age": 30}"""
|
|
100
|
+
val json = Json.parseUnsafe(jsonString)
|
|
101
|
+
|
|
102
|
+
// Transform
|
|
103
|
+
val updated = json.modify(p".age") {
|
|
104
|
+
case Json.Number(n) => Json.Number(n + 1)
|
|
105
|
+
case other => other
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Serialize
|
|
109
|
+
val output = updated.print(WriterConfig.withIndentionStep2)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Pattern 2: Diff and Apply
|
|
113
|
+
|
|
114
|
+
Compute changes and apply them:
|
|
115
|
+
|
|
116
|
+
```scala
|
|
117
|
+
import zio.blocks.schema.json.{Json, JsonPatch}
|
|
118
|
+
|
|
119
|
+
val original = Json.Object("count" -> Json.Number(0))
|
|
120
|
+
val target = Json.Object("count" -> Json.Number(1), "active" -> Json.Boolean(true))
|
|
121
|
+
|
|
122
|
+
// Compute the diff
|
|
123
|
+
val patch = JsonPatch.diff(original, target)
|
|
124
|
+
|
|
125
|
+
// Apply to reconstruct
|
|
126
|
+
val result = patch.apply(original)
|
|
127
|
+
// Right({"count": 1, "active": true})
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Pattern 3: Validate with Schema
|
|
131
|
+
|
|
132
|
+
Generate and validate using schemas:
|
|
133
|
+
|
|
134
|
+
```scala
|
|
135
|
+
import zio.blocks.schema._
|
|
136
|
+
import zio.blocks.schema.json.{Json, JsonSchema}
|
|
137
|
+
|
|
138
|
+
case class User(name: String, email: String, age: Int)
|
|
139
|
+
object User {
|
|
140
|
+
implicit val schema: Schema[User] = Schema.derived
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
val jsonSchema: JsonSchema = Schema[User].toJsonSchema
|
|
144
|
+
val validJson = Json.Object(
|
|
145
|
+
"name" -> Json.String("Alice"),
|
|
146
|
+
"email" -> Json.String("alice@example.com"),
|
|
147
|
+
"age" -> Json.Number(30)
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
jsonSchema.conforms(validJson) // true
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Pattern 4: Navigate and Extract with JsonSelection
|
|
154
|
+
|
|
155
|
+
Use fluent API to navigate nested structures and extract values:
|
|
156
|
+
|
|
157
|
+
```scala
|
|
158
|
+
import zio.blocks.schema._
|
|
159
|
+
import zio.blocks.schema.json.Json
|
|
160
|
+
|
|
161
|
+
val data = Json.Object(
|
|
162
|
+
"users" -> Json.Array(
|
|
163
|
+
Json.Object("name" -> Json.String("Alice"), "age" -> Json.Number(30)),
|
|
164
|
+
Json.Object("name" -> Json.String("Bob"), "age" -> Json.Number(25))
|
|
165
|
+
)
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
// Navigate to first user's age
|
|
169
|
+
val age: Either[SchemaError, Int] = data
|
|
170
|
+
.get("users") // JsonSelection
|
|
171
|
+
(0) // Navigate to first array element
|
|
172
|
+
.get("age") // Navigate to age field
|
|
173
|
+
.as[Int] // Decode to Int
|
|
174
|
+
|
|
175
|
+
// Check values
|
|
176
|
+
val ageValue = data.get("users")(0).get("age").one // Right(Json.Number(30))
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Pattern 5: Encode and Decode with Configuration
|
|
180
|
+
|
|
181
|
+
Control output formatting and parsing behavior:
|
|
182
|
+
|
|
183
|
+
```scala
|
|
184
|
+
import zio.blocks.schema._
|
|
185
|
+
import zio.blocks.schema.json.{Json, WriterConfig}
|
|
186
|
+
|
|
187
|
+
case class Config(host: String, port: Int)
|
|
188
|
+
object Config {
|
|
189
|
+
implicit val schema: Schema[Config] = Schema.derived
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
val config = Config("localhost", 8080)
|
|
193
|
+
val json = config.toJson
|
|
194
|
+
|
|
195
|
+
// Encode with custom formatting
|
|
196
|
+
val pretty = json.print(WriterConfig.withIndentionStep(2))
|
|
197
|
+
|
|
198
|
+
// Decode: parse back to Json, then decode
|
|
199
|
+
val parsed: Json = Json.parseUnsafe(pretty)
|
|
200
|
+
val config2: Either[SchemaError, Config] = parsed.as[Config]
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Pattern 6: Compose Multiple Patches
|
|
204
|
+
|
|
205
|
+
Chain multiple transformations into a single patch:
|
|
206
|
+
|
|
207
|
+
```scala
|
|
208
|
+
import zio.blocks.schema._
|
|
209
|
+
import zio.blocks.schema.json.{Json, JsonPatch}
|
|
210
|
+
|
|
211
|
+
val original = Json.Object("x" -> Json.Number(1), "y" -> Json.Number(2))
|
|
212
|
+
|
|
213
|
+
// Build patches independently
|
|
214
|
+
val patchX = JsonPatch.diff(
|
|
215
|
+
original,
|
|
216
|
+
original.set(p".x", Json.Number(10))
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
val patchY = JsonPatch.diff(
|
|
220
|
+
original.set(p".x", Json.Number(10)),
|
|
221
|
+
original.set(p".x", Json.Number(10)).set(p".y", Json.Number(20))
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
// Compose them
|
|
225
|
+
val combined = patchX ++ patchY
|
|
226
|
+
combined.apply(original)
|
|
227
|
+
// Right({"x": 10, "y": 20})
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## Integration Points
|
|
231
|
+
|
|
232
|
+
**With other codecs:** `Json` values convert to/from other formats (Avro, TOON, etc.) via `DynamicValue`:
|
|
233
|
+
|
|
234
|
+
```scala
|
|
235
|
+
import zio.blocks.schema.json.Json
|
|
236
|
+
import zio.blocks.schema.DynamicValue
|
|
237
|
+
|
|
238
|
+
val json = Json.parseUnsafe("""{"name": "Alice"}""")
|
|
239
|
+
val dynamic: DynamicValue = json.toDynamicValue
|
|
240
|
+
|
|
241
|
+
// Convert to other formats using DynamicValue
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
**With Schema system:** `Schema` enables automatic JSON encoding/decoding:
|
|
245
|
+
|
|
246
|
+
```scala
|
|
247
|
+
import zio.blocks.schema._
|
|
248
|
+
import zio.blocks.schema.json.Json
|
|
249
|
+
|
|
250
|
+
case class Person(name: String, age: Int)
|
|
251
|
+
object Person {
|
|
252
|
+
implicit val schema: Schema[Person] = Schema.derived
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Encode using Schema-based extension methods
|
|
256
|
+
val person = Person("Alice", 30)
|
|
257
|
+
val json = person.toJson
|
|
258
|
+
val jsonString = person.toJsonString
|
|
259
|
+
|
|
260
|
+
// Decode: parse and extract with Schema
|
|
261
|
+
val parsed: Json = Json.parseUnsafe(jsonString)
|
|
262
|
+
val decoded: Either[SchemaError, Person] = parsed.as[Person]
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
**With patching system:** `JsonPatch` and `JsonDiffer` integrate with generic `Patch` infrastructure:
|
|
266
|
+
|
|
267
|
+
```scala
|
|
268
|
+
import zio.blocks.schema.json.{Json, JsonPatch}
|
|
269
|
+
import zio.blocks.schema.patch.DynamicPatch
|
|
270
|
+
|
|
271
|
+
val jsonPatch: JsonPatch = JsonPatch.root(JsonPatch.Op.Set(Json.Null))
|
|
272
|
+
val dynamicPatch: DynamicPatch = jsonPatch.toDynamicPatch
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
**With optics and navigation:** `JsonSelection` provides query-like access complementary to `Optic` system:
|
|
276
|
+
|
|
277
|
+
- `JsonSelection` — runtime navigation through unknown JSON structures
|
|
278
|
+
- `Optic` — compile-time, type-safe navigation through known Scala types
|
|
279
|
+
- Both support nested access, filtering, and composition
|
|
280
|
+
|
|
281
|
+
**With validation:** `JsonSchema` integrates with Schema `Validation` system:
|
|
282
|
+
|
|
283
|
+
- Derive `JsonSchema` from Scala `Schema[A]`
|
|
284
|
+
- Validate raw JSON before type-safe decoding
|
|
285
|
+
- Compose with logical operators (allOf, anyOf, oneOf)
|
|
286
|
+
|
|
287
|
+
## Type Pages
|
|
288
|
+
|
|
289
|
+
- **[Json](./json.md)** — The core ADT representing JSON values with six cases (Object, Array, String, Number, Boolean, Null). Covers construction, navigation, modification, transformation, filtering, merging, and encoding.
|
|
290
|
+
|
|
291
|
+
- **[JsonPatch](./json-patch.md)** — Composable patches for transforming JSON values. Create patches via diff or manually, compose them with `++`, and apply with different failure modes (Strict, Lenient, Clobber).
|
|
292
|
+
|
|
293
|
+
- **[JsonDiffer](./json-differ.md)** — Diff algorithm computing minimal patches. Uses smart strategies per type: NumberDelta for numbers, LCS-based StringEdit for strings, ArrayEdit with insertion/deletion for arrays, and ObjectEdit for fields.
|
|
294
|
+
|
|
295
|
+
- **[JSON Schema](./json-schema.md)** — Full JSON Schema 2020-12 support for validation and code generation. Derive schemas from Scala types, validate JSON, construct schemas manually with builders, and combine with logical operators.
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: json-config
|
|
3
|
+
title: "JSON Configuration"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
The JSON codec module provides four configuration types for controlling encoding and decoding behavior: `WriterConfig`, `ReaderConfig`, `MergeStrategy`, and `NameMapper`. These types allow fine-grained control over how JSON is serialized and deserialized.
|
|
7
|
+
|
|
8
|
+
## WriterConfig
|
|
9
|
+
|
|
10
|
+
`WriterConfig` controls the formatting and content of encoded JSON output. Use it when calling `Json#print` on `Json` values or when encoding values with specific formatting requirements.
|
|
11
|
+
|
|
12
|
+
### Configuration Options
|
|
13
|
+
|
|
14
|
+
| Option | Type | Default | Purpose |
|
|
15
|
+
|--------------------|---------|---------|---------------------------------------------------------------|
|
|
16
|
+
| `indentionStep` | Int | 0 | Spaces per indentation level (0 = compact JSON) |
|
|
17
|
+
| `escapeUnicode` | Boolean | false | Escape non-ASCII characters as `\uXXXX` for ASCII-only output |
|
|
18
|
+
| `preferredBufSize` | Int | 32768 | Internal buffer size in bytes for streaming |
|
|
19
|
+
|
|
20
|
+
### Usage Examples
|
|
21
|
+
|
|
22
|
+
Pretty-print JSON output with configurable indentation:
|
|
23
|
+
|
|
24
|
+
```scala
|
|
25
|
+
import zio.blocks.schema._
|
|
26
|
+
import zio.blocks.schema.json.{WriterConfig}
|
|
27
|
+
|
|
28
|
+
case class Config(server: String, port: Int, debug: Boolean)
|
|
29
|
+
object Config { implicit val schema: Schema[Config] = Schema.derived }
|
|
30
|
+
|
|
31
|
+
val config = Config("localhost", 8080, true)
|
|
32
|
+
val json = config.toJson
|
|
33
|
+
|
|
34
|
+
// Pretty-printed with 2-space indentation
|
|
35
|
+
val pretty = json.print(WriterConfig.withIndentionStep(2))
|
|
36
|
+
// {
|
|
37
|
+
// "server": "localhost",
|
|
38
|
+
// "port": 8080,
|
|
39
|
+
// "debug": true
|
|
40
|
+
// }
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Configure custom indentation for JSON output:
|
|
44
|
+
|
|
45
|
+
```scala
|
|
46
|
+
import zio.blocks.schema._
|
|
47
|
+
import zio.blocks.schema.json.WriterConfig
|
|
48
|
+
|
|
49
|
+
case class User(name: String, email: String, age: Int)
|
|
50
|
+
object User { implicit val schema: Schema[User] = Schema.derived }
|
|
51
|
+
|
|
52
|
+
val user = User("Alice", "alice@example.com", 30)
|
|
53
|
+
val json = user.toJson
|
|
54
|
+
|
|
55
|
+
// 4-space indentation
|
|
56
|
+
val sorted = json.print(WriterConfig
|
|
57
|
+
.withIndentionStep(4)
|
|
58
|
+
)
|
|
59
|
+
// {
|
|
60
|
+
// "name": "Alice",
|
|
61
|
+
// "email": "alice@example.com",
|
|
62
|
+
// "age": 30
|
|
63
|
+
// }
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Escape non-ASCII characters for ASCII-only transmission:
|
|
67
|
+
|
|
68
|
+
```scala
|
|
69
|
+
import zio.blocks.schema._
|
|
70
|
+
import zio.blocks.schema.json.WriterConfig
|
|
71
|
+
|
|
72
|
+
case class Message(text: String)
|
|
73
|
+
object Message { implicit val schema: Schema[Message] = Schema.derived }
|
|
74
|
+
|
|
75
|
+
val msg = Message("Hello 世界")
|
|
76
|
+
val json = msg.toJson
|
|
77
|
+
|
|
78
|
+
// Escape non-ASCII characters - non-ASCII become \uXXXX sequences
|
|
79
|
+
val ascii = json.print(WriterConfig.withEscapeUnicode(true))
|
|
80
|
+
// Output: {"text":"Hello \\u4e16\\u754c"}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## ReaderConfig
|
|
84
|
+
|
|
85
|
+
`ReaderConfig` controls how JSON input is parsed and validated during decoding. Use it when calling `Json.parse()` with custom parsing behavior.
|
|
86
|
+
|
|
87
|
+
### Configuration Options
|
|
88
|
+
|
|
89
|
+
| Option | Type | Default | Purpose |
|
|
90
|
+
|------------------------|---------|---------|----------------------------------------|
|
|
91
|
+
| `checkForEndOfInput` | Boolean | true | Verify no extra input after valid JSON |
|
|
92
|
+
| `preferredCharBufSize` | Int | 8192 | Internal character buffer size |
|
|
93
|
+
|
|
94
|
+
### Usage Examples
|
|
95
|
+
|
|
96
|
+
Allow trailing whitespace after valid JSON during parsing:
|
|
97
|
+
|
|
98
|
+
```scala
|
|
99
|
+
import zio.blocks.schema._
|
|
100
|
+
import zio.blocks.schema.json.{Json, ReaderConfig}
|
|
101
|
+
|
|
102
|
+
// Lenient: allow trailing whitespace
|
|
103
|
+
val lenientConfig = ReaderConfig.withCheckForEndOfInput(false)
|
|
104
|
+
|
|
105
|
+
val jsonString = """{"name": "Alice"} """ // Trailing spaces
|
|
106
|
+
val result = Json.parse(jsonString, lenientConfig)
|
|
107
|
+
// Right(Json.Object(...))
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## MergeStrategy
|
|
111
|
+
|
|
112
|
+
`MergeStrategy` determines how field conflicts are resolved when merging two JSON objects. It provides strategies for different use cases, from strict validation to lenient concatenation.
|
|
113
|
+
|
|
114
|
+
**Available Strategies:**
|
|
115
|
+
- `Strict` — Fails if the same field appears in both objects
|
|
116
|
+
- `Right` — The right object's values override the left's
|
|
117
|
+
- `Left` — The left object's values take precedence
|
|
118
|
+
- Additional strategies may be available depending on your use case
|
|
119
|
+
|
|
120
|
+
For details on merge strategies and their usage, see the Merging section of the [Json](./json.md) documentation.
|
|
121
|
+
|
|
122
|
+
## NameMapper
|
|
123
|
+
|
|
124
|
+
`NameMapper` customizes how field names are transformed between Scala types and JSON representation. It enables patterns like camelCase ↔ snake_case conversion.
|
|
125
|
+
|
|
126
|
+
### Built-in Mappers
|
|
127
|
+
|
|
128
|
+
Use identity mapping to keep field names unchanged:
|
|
129
|
+
|
|
130
|
+
```scala
|
|
131
|
+
import zio.blocks.schema._
|
|
132
|
+
|
|
133
|
+
case class User(firstName: String, lastName: String)
|
|
134
|
+
object User {
|
|
135
|
+
implicit val schema: Schema[User] = Schema.derived
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Default: no transformation
|
|
139
|
+
val user = User("Alice", "Smith")
|
|
140
|
+
val json = user.toJson
|
|
141
|
+
// {"firstName": "Alice", "lastName": "Smith"}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**Snake Case:**
|
|
145
|
+
Convert camelCase to snake_case:
|
|
146
|
+
|
|
147
|
+
```scala
|
|
148
|
+
import zio.blocks.schema._
|
|
149
|
+
|
|
150
|
+
case class User(firstName: String, lastName: String)
|
|
151
|
+
object User {
|
|
152
|
+
implicit val schema: Schema[User] = Schema.derived
|
|
153
|
+
implicit val nameMapper: NameMapper = NameMapper.SnakeCase
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
val user = User("Alice", "Smith")
|
|
157
|
+
val json = user.toJson
|
|
158
|
+
// {"first_name": "Alice", "last_name": "Smith"}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
**Kebab Case:**
|
|
162
|
+
Convert camelCase to kebab-case:
|
|
163
|
+
|
|
164
|
+
```scala
|
|
165
|
+
import zio.blocks.schema._
|
|
166
|
+
|
|
167
|
+
case class User(firstName: String, lastName: String)
|
|
168
|
+
object User {
|
|
169
|
+
implicit val schema: Schema[User] = Schema.derived
|
|
170
|
+
implicit val nameMapper: NameMapper = NameMapper.KebabCase
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
val user = User("Alice", "Smith")
|
|
174
|
+
val json = user.toJson
|
|
175
|
+
// {"first-name": "Alice", "last-name": "Smith"}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
**Custom Mappers:**
|
|
179
|
+
Additional custom name mappers may be available depending on your needs. The built-in mappers (Identity, SnakeCase, KebabCase) cover most common use cases.
|
|
180
|
+
|
|
181
|
+
## Combining Configuration
|
|
182
|
+
|
|
183
|
+
Use multiple configuration options together for fine-grained control:
|
|
184
|
+
|
|
185
|
+
```scala
|
|
186
|
+
import zio.blocks.schema._
|
|
187
|
+
import zio.blocks.schema.json.WriterConfig
|
|
188
|
+
|
|
189
|
+
case class Config(server: String, port: Int, timeout: Option[Int])
|
|
190
|
+
object Config { implicit val schema: Schema[Config] = Schema.derived }
|
|
191
|
+
|
|
192
|
+
val config = Config("example.com", 443, None)
|
|
193
|
+
val json = config.toJson
|
|
194
|
+
|
|
195
|
+
// Pretty-printed with 4-space indentation
|
|
196
|
+
val output = json.print(WriterConfig
|
|
197
|
+
.withIndentionStep(4)
|
|
198
|
+
)
|
|
199
|
+
// {
|
|
200
|
+
// "server": "example.com",
|
|
201
|
+
// "port": 443
|
|
202
|
+
// }
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Performance Implications
|
|
206
|
+
|
|
207
|
+
- **WriterConfig:** Indentation increases output size but has minimal performance impact
|
|
208
|
+
- **ReaderConfig:** `checkForEndOfInput` adds one comparison
|
|
209
|
+
- **MergeStrategy:** Strategy choice affects merge performance based on use case
|
|
210
|
+
- **NameMapper:** Applied once per field during schema derivation; zero runtime cost if identity mapping
|
|
211
|
+
|
|
212
|
+
## Best Practices
|
|
213
|
+
|
|
214
|
+
1. **Use WriterConfig for human-readable output only** — Compact output is faster for wire transmission
|
|
215
|
+
2. **Choose MergeStrategy based on use case** — Strict for validation, others for defaults
|
|
216
|
+
3. **Standardize on one NameMapper** across your codebase to avoid confusion
|
|
217
|
+
4. **Cache WriterConfig/ReaderConfig instances** — They're immutable and can be reused
|
|
@@ -338,7 +338,7 @@ combinedPatch.apply(personJson)
|
|
|
338
338
|
|
|
339
339
|
### Converting
|
|
340
340
|
|
|
341
|
-
`toDynamicPatch` converts a `JsonPatch` to a
|
|
341
|
+
`toDynamicPatch` converts a `JsonPatch` to a `DynamicPatch`. This is always safe — every JSON operation maps to a corresponding dynamic operation. `NumberDelta` widens to `BigDecimalDelta`:
|
|
342
342
|
|
|
343
343
|
```scala
|
|
344
344
|
case class JsonPatch(ops: Chunk[JsonPatch.JsonPatchOp]) {
|
|
@@ -386,7 +386,7 @@ modeJson.patch(modePatch, PatchMode.Strict)
|
|
|
386
386
|
// SchemaError(
|
|
387
387
|
// List(
|
|
388
388
|
// ExpectationMismatch(
|
|
389
|
-
// source = DynamicOptic(
|
|
389
|
+
// source = DynamicOptic(ArraySeq()),
|
|
390
390
|
// expectation = "Key 'a' already exists in object"
|
|
391
391
|
// )
|
|
392
392
|
// )
|
|
@@ -723,7 +723,7 @@ fieldPatch.apply(doc)
|
|
|
723
723
|
// res40: Either[SchemaError, Json] = Left(
|
|
724
724
|
// SchemaError(
|
|
725
725
|
// List(
|
|
726
|
-
// MissingField(source = DynamicOptic(
|
|
726
|
+
// MissingField(source = DynamicOptic(ArraySeq()), fieldName = "updatedAt")
|
|
727
727
|
// )
|
|
728
728
|
// )
|
|
729
729
|
// )
|
|
@@ -769,7 +769,7 @@ val dynPatch: DynamicPatch = jsonPatch.toDynamicPatch
|
|
|
769
769
|
val back: Either[SchemaError, JsonPatch] = JsonPatch.fromDynamicPatch(dynPatch)
|
|
770
770
|
```
|
|
771
771
|
|
|
772
|
-
|
|
772
|
+
For the typed `Patch[S]` API, see the Patching documentation.
|
|
773
773
|
|
|
774
774
|
### Serialization
|
|
775
775
|
|
|
@@ -782,7 +782,7 @@ import zio.blocks.schema.Schema
|
|
|
782
782
|
val schema: Schema[JsonPatch] = implicitly[Schema[JsonPatch]]
|
|
783
783
|
```
|
|
784
784
|
|
|
785
|
-
|
|
785
|
+
For codec derivation and usage details, see the Codec & Format documentation.
|
|
786
786
|
|
|
787
787
|
## Examples
|
|
788
788
|
|
|
@@ -27,9 +27,7 @@ Key features:
|
|
|
27
27
|
|
|
28
28
|
## Deriving JSON Schema from Schema
|
|
29
29
|
|
|
30
|
-
The most common use case is deriving a JSON Schema from an existing `Schema[A]`.
|
|
31
|
-
|
|
32
|
-
### Basic Derivation
|
|
30
|
+
The most common use case is deriving a JSON Schema from an existing `Schema[A]`. You can derive directly from the schema or through a `JsonCodec` for more control.
|
|
33
31
|
|
|
34
32
|
```scala
|
|
35
33
|
import zio.blocks.schema._
|
|
@@ -40,9 +38,13 @@ object Person {
|
|
|
40
38
|
implicit val schema: Schema[Person] = Schema.derived
|
|
41
39
|
}
|
|
42
40
|
|
|
43
|
-
//
|
|
41
|
+
// Direct derivation
|
|
44
42
|
val jsonSchema: JsonSchema = Schema[Person].toJsonSchema
|
|
45
43
|
|
|
44
|
+
// Or derive through JsonCodec for more control
|
|
45
|
+
val codec = Schema[Person].derive(JsonFormat)
|
|
46
|
+
val jsonSchema2 = codec.toJsonSchema
|
|
47
|
+
|
|
46
48
|
// The derived schema validates JSON values
|
|
47
49
|
val valid = Json.Object("name" -> Json.String("Alice"), "age" -> Json.Number(30))
|
|
48
50
|
val invalid = Json.Object("name" -> Json.Number(123))
|
|
@@ -51,24 +53,6 @@ jsonSchema.conforms(valid) // true
|
|
|
51
53
|
jsonSchema.conforms(invalid) // false
|
|
52
54
|
```
|
|
53
55
|
|
|
54
|
-
### Through JsonCodec
|
|
55
|
-
|
|
56
|
-
For more control, derive through `JsonCodec`:
|
|
57
|
-
|
|
58
|
-
```scala
|
|
59
|
-
import zio.blocks.schema._
|
|
60
|
-
import zio.blocks.schema.json._
|
|
61
|
-
|
|
62
|
-
case class User(email: String, active: Boolean)
|
|
63
|
-
object User {
|
|
64
|
-
implicit val schema: Schema[User] = Schema.derived
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Derive codec first, then get JSON Schema
|
|
68
|
-
val codec = Schema[User].derive(JsonFormat)
|
|
69
|
-
val jsonSchema = codec.toJsonSchema
|
|
70
|
-
```
|
|
71
|
-
|
|
72
56
|
## Creating Schemas
|
|
73
57
|
|
|
74
58
|
### Boolean Schemas
|
|
@@ -197,22 +181,14 @@ Create schemas for object validation:
|
|
|
197
181
|
import zio.blocks.schema.json.{JsonSchema, JsonSchemaType}
|
|
198
182
|
import zio.blocks.chunk.ChunkMap
|
|
199
183
|
|
|
200
|
-
// Object with properties
|
|
184
|
+
// Object with properties and constraints
|
|
201
185
|
val person = JsonSchema.obj(
|
|
202
|
-
properties = Some(ChunkMap(
|
|
203
|
-
"name" -> JsonSchema.ofType(JsonSchemaType.String),
|
|
204
|
-
"age" -> JsonSchema.ofType(JsonSchemaType.Integer)
|
|
205
|
-
)),
|
|
206
|
-
required = Some(Set("name"))
|
|
207
|
-
)
|
|
208
|
-
|
|
209
|
-
// Object with no additional properties
|
|
210
|
-
val strictPerson = JsonSchema.obj(
|
|
211
186
|
properties = Some(ChunkMap(
|
|
212
187
|
"name" -> JsonSchema.ofType(JsonSchemaType.String),
|
|
213
188
|
"age" -> JsonSchema.ofType(JsonSchemaType.Integer)
|
|
214
189
|
)),
|
|
215
190
|
required = Some(Set("name")),
|
|
191
|
+
// Reject properties not defined above
|
|
216
192
|
additionalProperties = Some(JsonSchema.False)
|
|
217
193
|
)
|
|
218
194
|
```
|
|
@@ -320,7 +296,7 @@ val paymentSchema = JsonSchema.Object(
|
|
|
320
296
|
### Basic Validation
|
|
321
297
|
|
|
322
298
|
```scala
|
|
323
|
-
import zio.blocks.schema.json.{JsonSchema, Json, JsonSchemaType}
|
|
299
|
+
import zio.blocks.schema.json.{JsonSchema, Json, JsonSchemaType, ValidationOptions}
|
|
324
300
|
import zio.blocks.chunk.ChunkMap
|
|
325
301
|
|
|
326
302
|
val schema = JsonSchema.obj(
|
|
@@ -347,25 +323,16 @@ schema.check(invalidJson) // Some(SchemaError(...))
|
|
|
347
323
|
// Using conforms() - returns Boolean
|
|
348
324
|
schema.conforms(validJson) // true
|
|
349
325
|
schema.conforms(invalidJson) // false
|
|
350
|
-
```
|
|
351
|
-
|
|
352
|
-
### Validation Options
|
|
353
|
-
|
|
354
|
-
Control validation behavior:
|
|
355
|
-
|
|
356
|
-
```scala
|
|
357
|
-
import zio.blocks.schema.json.{JsonSchema, Json, ValidationOptions}
|
|
358
326
|
|
|
359
|
-
|
|
360
|
-
val
|
|
327
|
+
// Control validation behavior with ValidationOptions
|
|
328
|
+
val emailSchema = JsonSchema.string(format = Some("email"))
|
|
329
|
+
val invalidEmail = Json.String("not-an-email")
|
|
361
330
|
|
|
362
331
|
// With format validation (default)
|
|
363
|
-
|
|
364
|
-
schema.check(value, strictOptions) // Some(error)
|
|
332
|
+
emailSchema.check(invalidEmail, ValidationOptions.formatAssertion) // Some(error)
|
|
365
333
|
|
|
366
334
|
// Without format validation (format as annotation only)
|
|
367
|
-
|
|
368
|
-
schema.check(value, lenientOptions) // None
|
|
335
|
+
emailSchema.check(invalidEmail, ValidationOptions.annotationOnly) // None
|
|
369
336
|
```
|
|
370
337
|
|
|
371
338
|
### Error Messages
|