@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,322 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: json-selection
|
|
3
|
+
title: "JsonSelection"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
`JsonSelection` is a fluent wrapper type that enables composable, chainable navigation through JSON structures. It wraps `Either[SchemaError, Chunk[Json]]`, allowing operations that may fail gracefully or return multiple values.
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
`JsonSelection` makes it easy to navigate unknown or deeply nested JSON at runtime without needing to match on `Either` at each step. Chain operations like `JsonSelection#get`, `JsonSelection#apply`, `JsonSelection#filter`, and `JsonSelection#as` to build powerful queries.
|
|
11
|
+
|
|
12
|
+
**Key characteristics:**
|
|
13
|
+
- **Fluent chaining:** Operations chain naturally without unwrapping intermediate results
|
|
14
|
+
- **Multi-value support:** Can contain zero, one, or many `Json` values
|
|
15
|
+
- **Error propagation:** Errors short-circuit further operations; querying an error selection returns the same error
|
|
16
|
+
- **Type extraction:** Use `.as[Type]` to decode to Scala types with Schema-based derivation
|
|
17
|
+
|
|
18
|
+
## Creating JsonSelection
|
|
19
|
+
|
|
20
|
+
You can create `JsonSelection` instances in multiple ways: from existing `Json` values, or using companion object constructors. Each approach is useful for different scenarios—direct navigation for existing JSON, and explicit construction for building selections programmatically.
|
|
21
|
+
|
|
22
|
+
### From Json Values
|
|
23
|
+
|
|
24
|
+
Create a `JsonSelection` directly from a `Json` value by calling navigation methods:
|
|
25
|
+
|
|
26
|
+
```scala
|
|
27
|
+
import zio.blocks.schema.json.{Json, JsonSelection}
|
|
28
|
+
|
|
29
|
+
val json = Json.parseUnsafe("""{"name": "Alice"}""")
|
|
30
|
+
|
|
31
|
+
// Get a single field
|
|
32
|
+
val name: JsonSelection = json.get("name")
|
|
33
|
+
|
|
34
|
+
// Get array element by index
|
|
35
|
+
val values = Json.parseUnsafe("[1, 2, 3]")
|
|
36
|
+
val selected: JsonSelection = values.get(0)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### From Companion Object
|
|
40
|
+
|
|
41
|
+
Construct `JsonSelection` instances programmatically using the companion object methods for empty, successful, and failed selections:
|
|
42
|
+
|
|
43
|
+
```scala
|
|
44
|
+
import zio.blocks.schema._
|
|
45
|
+
import zio.blocks.schema.json.{Json, JsonSelection}
|
|
46
|
+
|
|
47
|
+
// Empty successful selection
|
|
48
|
+
val empty = JsonSelection.empty
|
|
49
|
+
|
|
50
|
+
// Succeed with a single value
|
|
51
|
+
val success = JsonSelection.succeed(Json.String("hello"))
|
|
52
|
+
|
|
53
|
+
// Succeed with multiple values
|
|
54
|
+
val many = JsonSelection.succeedMany(
|
|
55
|
+
zio.blocks.chunk.Chunk.from(Seq(Json.Number(1), Json.Number(2)))
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
// Fail with an error
|
|
59
|
+
val failure = JsonSelection.fail(SchemaError("not found"))
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Navigation Operations
|
|
63
|
+
|
|
64
|
+
Navigate through JSON structures using three complementary approaches: field access for object properties, array indexing for elements, and path expressions for complex nested navigation.
|
|
65
|
+
|
|
66
|
+
### Field Navigation
|
|
67
|
+
|
|
68
|
+
Navigate to object fields using `JsonSelection#get` with a field name:
|
|
69
|
+
|
|
70
|
+
```scala
|
|
71
|
+
import zio.blocks.schema.json.Json
|
|
72
|
+
|
|
73
|
+
val data = Json.parseUnsafe("""{
|
|
74
|
+
"user": {
|
|
75
|
+
"name": "Bob",
|
|
76
|
+
"email": "bob@example.com"
|
|
77
|
+
}
|
|
78
|
+
}""")
|
|
79
|
+
|
|
80
|
+
// Single field access
|
|
81
|
+
val user = data.get("user")
|
|
82
|
+
|
|
83
|
+
// Chained field access
|
|
84
|
+
val name = data.get("user").get("name")
|
|
85
|
+
|
|
86
|
+
// Multiple levels of nesting
|
|
87
|
+
val email = data.get("user").get("email")
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Array Navigation
|
|
91
|
+
|
|
92
|
+
Access array elements by index using `JsonSelection#apply` or `JsonSelection#get`:
|
|
93
|
+
|
|
94
|
+
```scala
|
|
95
|
+
import zio.blocks.schema.json.Json
|
|
96
|
+
|
|
97
|
+
val data = Json.parseUnsafe("""[
|
|
98
|
+
{"id": 1, "name": "Alice"},
|
|
99
|
+
{"id": 2, "name": "Bob"}
|
|
100
|
+
]""")
|
|
101
|
+
|
|
102
|
+
// Index access (0-based)
|
|
103
|
+
val first = data.get(0)
|
|
104
|
+
|
|
105
|
+
// Chained index and field access
|
|
106
|
+
val firstName = data.get(0).get("name")
|
|
107
|
+
val secondId = data.get(1).get("id")
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Path-Based Navigation
|
|
111
|
+
|
|
112
|
+
Use path interpolators (e.g., `p".company.employees[0].name"`) to navigate deeply nested structures in a single operation:
|
|
113
|
+
|
|
114
|
+
```scala
|
|
115
|
+
import zio.blocks.schema._
|
|
116
|
+
import zio.blocks.schema.json.Json
|
|
117
|
+
|
|
118
|
+
val data = Json.Object(
|
|
119
|
+
"company" -> Json.Object(
|
|
120
|
+
"employees" -> Json.Array(
|
|
121
|
+
Json.Object("name" -> Json.String("Alice"), "department" -> Json.String("Engineering")),
|
|
122
|
+
Json.Object("name" -> Json.String("Bob"), "department" -> Json.String("Sales"))
|
|
123
|
+
)
|
|
124
|
+
)
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
// Navigate using path interpolator
|
|
128
|
+
val path = p".company.employees[0].name"
|
|
129
|
+
val firstEmpName = data.get(path) // JsonSelection(Right(Chunk(Json.String("Alice"))))
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Filtering and Querying
|
|
133
|
+
|
|
134
|
+
Reduce selections to only the values you need by filtering by JSON type or custom predicates. This enables working with heterogeneous JSON arrays where values may be of different types.
|
|
135
|
+
|
|
136
|
+
### Filter by Type
|
|
137
|
+
|
|
138
|
+
Keep only values of a specific JSON type using type-filtering methods like `JsonSelection#strings`, `JsonSelection#numbers`, and `JsonSelection#booleans`:
|
|
139
|
+
|
|
140
|
+
```scala
|
|
141
|
+
import zio.blocks.schema.json.{Json, JsonSelection}
|
|
142
|
+
|
|
143
|
+
val mixed = Json.parseUnsafe("""["text", 42, true, "more text"]""")
|
|
144
|
+
|
|
145
|
+
// Create a selection containing all array elements, then filter by type
|
|
146
|
+
val allElements = JsonSelection.succeedMany(mixed.elements)
|
|
147
|
+
val strings = allElements.strings // JsonSelection with string values
|
|
148
|
+
val numbers = allElements.numbers // JsonSelection with number values
|
|
149
|
+
val booleans = allElements.booleans // JsonSelection with boolean values
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Filter with Predicate
|
|
153
|
+
|
|
154
|
+
Use custom predicates with `JsonSelection#filter` to keep only values that match specific conditions:
|
|
155
|
+
|
|
156
|
+
```scala
|
|
157
|
+
import zio.blocks.schema.json.{Json, JsonSelection}
|
|
158
|
+
|
|
159
|
+
val data = Json.parseUnsafe("[1, 2, 3, 4]")
|
|
160
|
+
|
|
161
|
+
// Create selection and filter with predicate
|
|
162
|
+
val allElements = JsonSelection.succeedMany(data.elements)
|
|
163
|
+
val evenOnly = allElements.filter { json =>
|
|
164
|
+
json match {
|
|
165
|
+
case Json.Number(n) => n.toInt % 2 == 0
|
|
166
|
+
case _ => false
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Extracting Values
|
|
172
|
+
|
|
173
|
+
Extract concrete values from selections by decoding them to Scala types, checking single vs. multiple values, or inspecting selection state without extraction.
|
|
174
|
+
|
|
175
|
+
### Type Decoding
|
|
176
|
+
|
|
177
|
+
Decode a single selected value to a Scala type using `JsonSelection#as`, which fails if more than one value is selected:
|
|
178
|
+
|
|
179
|
+
```scala
|
|
180
|
+
import zio.blocks.schema._
|
|
181
|
+
import zio.blocks.schema.json.Json
|
|
182
|
+
|
|
183
|
+
// Decode to Scala types
|
|
184
|
+
val selection = Json.parseUnsafe("""{"count": 42}""")
|
|
185
|
+
|
|
186
|
+
val count: Either[SchemaError, Int] = selection.get("count").as[Int]
|
|
187
|
+
val str: Either[SchemaError, String] = selection.get("count").as[String] // Left (type mismatch)
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Multiple Decoding
|
|
191
|
+
|
|
192
|
+
Decode all selected values to a collection using `JsonSelection#asAll`, which succeeds even if the selection is empty:
|
|
193
|
+
|
|
194
|
+
```scala
|
|
195
|
+
import zio.blocks.schema._
|
|
196
|
+
import zio.blocks.schema.json.{Json, JsonSelection}
|
|
197
|
+
|
|
198
|
+
val data = Json.parseUnsafe("""["Alice", "Bob", "Charlie"]""")
|
|
199
|
+
|
|
200
|
+
// Create selection of all array elements and decode
|
|
201
|
+
val allElements = JsonSelection.succeedMany(data.elements)
|
|
202
|
+
val names: Either[SchemaError, Seq[String]] = allElements.asAll[String].map(_.toSeq)
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Extract Single Value
|
|
206
|
+
|
|
207
|
+
Use `JsonSelection#one` to extract exactly one value, failing if the selection contains zero or more than one value:
|
|
208
|
+
|
|
209
|
+
```scala
|
|
210
|
+
import zio.blocks.schema._
|
|
211
|
+
import zio.blocks.schema.json.{Json, JsonSelection}
|
|
212
|
+
|
|
213
|
+
val arr = Json.parseUnsafe("[1, 2]")
|
|
214
|
+
|
|
215
|
+
// Get exactly one value from a selection (fails if 0 or more than 1)
|
|
216
|
+
val single = arr.get(0).one // Right(Json.Number(1))
|
|
217
|
+
val multiple = JsonSelection.succeedMany(arr.elements).one // Left(SchemaError(...))
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Check and Get
|
|
221
|
+
|
|
222
|
+
Inspect selection state without extraction using properties like `JsonSelection#values`, `JsonSelection#error`, `JsonSelection#isSuccess`, and `JsonSelection#isFailure`:
|
|
223
|
+
|
|
224
|
+
```scala
|
|
225
|
+
import zio.blocks.schema.json.{Json, JsonSelection}
|
|
226
|
+
|
|
227
|
+
val data = Json.Object("x" -> Json.Number(1))
|
|
228
|
+
|
|
229
|
+
// Safe option extraction
|
|
230
|
+
val x: Option[Json] = data.get("x").values.flatMap(_.headOption)
|
|
231
|
+
|
|
232
|
+
// Check if selection succeeded
|
|
233
|
+
val success = data.get("exists").isSuccess // true if "exists" field found
|
|
234
|
+
val failed = data.get("notFound").isFailure // true if field not found
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Size and Existence Checks
|
|
238
|
+
|
|
239
|
+
Query selection size and emptiness using `JsonSelection#size`, `JsonSelection#isEmpty`, and `JsonSelection#nonEmpty`:
|
|
240
|
+
|
|
241
|
+
```scala
|
|
242
|
+
import zio.blocks.schema.json.{Json, JsonSelection}
|
|
243
|
+
|
|
244
|
+
val data = Json.parseUnsafe("[1, 2, 3]")
|
|
245
|
+
|
|
246
|
+
// Create selection and check size
|
|
247
|
+
val selection = JsonSelection.succeedMany(data.elements)
|
|
248
|
+
val size = selection.size // 3
|
|
249
|
+
val isEmpty = selection.isEmpty // false
|
|
250
|
+
val nonEmpty = selection.nonEmpty // true
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
## Modifying Selections
|
|
254
|
+
|
|
255
|
+
Use `JsonSelection` to update values at specific paths using `set` for replacement or `modify` for transformation:
|
|
256
|
+
|
|
257
|
+
```scala
|
|
258
|
+
import zio.blocks.schema._
|
|
259
|
+
import zio.blocks.schema.json.Json
|
|
260
|
+
|
|
261
|
+
val original = Json.Object("count" -> Json.Number(0))
|
|
262
|
+
|
|
263
|
+
// Set a new value
|
|
264
|
+
val updated = original.set(p".count", Json.Number(1))
|
|
265
|
+
|
|
266
|
+
// Modify with a function
|
|
267
|
+
val modified = original.modify(p".count") {
|
|
268
|
+
case Json.Number(n) => Json.Number(n + 1)
|
|
269
|
+
case other => other
|
|
270
|
+
}
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## Error Handling
|
|
274
|
+
|
|
275
|
+
Selections propagate errors through the chain:
|
|
276
|
+
|
|
277
|
+
```scala
|
|
278
|
+
import zio.blocks.schema._
|
|
279
|
+
import zio.blocks.schema.json.Json
|
|
280
|
+
|
|
281
|
+
val data = Json.Object("name" -> Json.String("Alice"))
|
|
282
|
+
|
|
283
|
+
// Missing field returns error
|
|
284
|
+
val missing = data.get("age").error // Some(SchemaError("field not found"))
|
|
285
|
+
|
|
286
|
+
// Chain continues with error
|
|
287
|
+
val stillMissing = data.get("age").get("nested") // Still carries the error
|
|
288
|
+
|
|
289
|
+
// Check error state
|
|
290
|
+
val result = data.get("x")
|
|
291
|
+
val maybeError: Option[SchemaError] = result.error
|
|
292
|
+
val maybeValues = result.values // None if error
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
## Integration with Codecs
|
|
296
|
+
|
|
297
|
+
`JsonSelection` integrates seamlessly with `JsonCodec` and `Schema`:
|
|
298
|
+
|
|
299
|
+
```scala
|
|
300
|
+
import zio.blocks.schema._
|
|
301
|
+
import zio.blocks.schema.json.{Json, JsonSelection}
|
|
302
|
+
import zio.blocks.chunk.Chunk
|
|
303
|
+
|
|
304
|
+
case class User(name: String, email: String)
|
|
305
|
+
object User { implicit val schema: Schema[User] = Schema.derived }
|
|
306
|
+
|
|
307
|
+
val users = Json.parseUnsafe("""[
|
|
308
|
+
{"name": "Alice", "email": "alice@example.com"},
|
|
309
|
+
{"name": "Bob", "email": "bob@example.com"}
|
|
310
|
+
]""")
|
|
311
|
+
|
|
312
|
+
// Navigate and decode in one chain
|
|
313
|
+
val firstUser: Either[SchemaError, User] = users.get(0).as[User]
|
|
314
|
+
val allUsers: Either[SchemaError, Chunk[User]] = JsonSelection.succeedMany(users.elements).asAll[User]
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
## Performance Notes
|
|
318
|
+
|
|
319
|
+
- **Zero-allocation navigation:** `JsonSelection` itself is a value type (AnyVal) and compiles to no allocation
|
|
320
|
+
- **Lazy chaining:** Operations chain without intermediate allocations; only final extraction materializes values
|
|
321
|
+
- **Error short-circuiting:** Failed selections don't execute further operations
|
|
322
|
+
- **Streaming-friendly:** For large JSON, use `JsonSelection#get` selectively rather than iterating over all values
|
|
@@ -3,7 +3,7 @@ id: json
|
|
|
3
3
|
title: "Json"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
`Json` is
|
|
6
|
+
`Json` is a type-safe, schema-free representation of JSON values that enables navigation, transformation, merging, and querying without losing fidelity.
|
|
7
7
|
|
|
8
8
|
## Overview
|
|
9
9
|
|
|
@@ -212,7 +212,8 @@ result.isFailure // false
|
|
|
212
212
|
import zio.blocks.schema.json.{Json, JsonSelection}
|
|
213
213
|
import zio.blocks.schema.SchemaError
|
|
214
214
|
|
|
215
|
-
val
|
|
215
|
+
val json = Json.parseUnsafe("""{"name": "Alice", "age": 30}""")
|
|
216
|
+
val selection: JsonSelection = json.get("name")
|
|
216
217
|
|
|
217
218
|
// Get single value (exactly one required)
|
|
218
219
|
val oneValue: Either[SchemaError, Json] = selection.one
|
|
@@ -557,41 +558,9 @@ Schema[java.time.ZonedDateTime].jsonCodec
|
|
|
557
558
|
Schema[java.util.UUID].jsonCodec
|
|
558
559
|
```
|
|
559
560
|
|
|
560
|
-
### Encoding/Decoding of Primitives
|
|
561
|
-
|
|
562
|
-
```scala
|
|
563
|
-
import zio.blocks.schema._
|
|
564
|
-
|
|
565
|
-
// Encode Scala values to Json
|
|
566
|
-
val intJson = 42.toJson // Json.Number(42)
|
|
567
|
-
val strJson = "hello".toJson // Json.String("hello")
|
|
568
|
-
|
|
569
|
-
// Decode Json to Scala values
|
|
570
|
-
val intResult = intJson.as[Int] // Right(42)
|
|
571
|
-
val strResult = strJson.as[String] // Right("hello")
|
|
572
|
-
```
|
|
573
|
-
|
|
574
|
-
### Encoding/Decoding of Case Classes
|
|
575
|
-
|
|
576
|
-
For complex types, use Schema-based derivation:
|
|
577
|
-
|
|
578
|
-
```scala
|
|
579
|
-
import zio.blocks.schema._
|
|
580
|
-
|
|
581
|
-
case class Person(name: String, age: Int)
|
|
582
|
-
|
|
583
|
-
object Person {
|
|
584
|
-
implicit val schema: Schema[Person] = Schema.derived
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
val person = Person("Alice", 30)
|
|
588
|
-
val json = person.toJson
|
|
589
|
-
val decoded = json.as[Person]
|
|
590
|
-
```
|
|
591
|
-
|
|
592
561
|
### Extension Syntax
|
|
593
562
|
|
|
594
|
-
When a `Schema` is in scope, you can use convenient extension methods
|
|
563
|
+
When a `Schema` is in scope, you can use convenient extension methods on any Scala value to encode and decode JSON. These work on primitives, case classes, and all other types:
|
|
595
564
|
|
|
596
565
|
```scala
|
|
597
566
|
import zio.blocks.schema._
|
|
@@ -617,30 +586,15 @@ val parsed = """{"name":"Bob","age":25}""".fromJson[Person] // Right(Person("Bo
|
|
|
617
586
|
|
|
618
587
|
// Parse from bytes
|
|
619
588
|
val fromBytes = jsonBytes.fromJson[Person] // Right(Person("Alice", 30))
|
|
620
|
-
```
|
|
621
589
|
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
```scala
|
|
627
|
-
import zio.blocks.schema._
|
|
628
|
-
import zio.blocks.schema.json._
|
|
629
|
-
|
|
630
|
-
case class Person(name: String, age: Int)
|
|
631
|
-
object Person {
|
|
632
|
-
implicit val schema: Schema[Person] = Schema.derived
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
val json = Json.parseUnsafe("""{"name": "Alice", "age": 30}""")
|
|
636
|
-
|
|
637
|
-
// Decode to a specific type
|
|
638
|
-
val person: Either[SchemaError, Person] = json.as[Person]
|
|
639
|
-
|
|
640
|
-
// Unsafe version (throws on error)
|
|
641
|
-
val personUnsafe: Person = json.asUnsafe[Person]
|
|
590
|
+
// Works on primitives too
|
|
591
|
+
val intJson = 42.toJson // Json.Number(42)
|
|
592
|
+
val strJson = "hello".toJson // Json.String("hello")
|
|
593
|
+
val intResult = intJson.as[Int] // Right(42)
|
|
642
594
|
```
|
|
643
595
|
|
|
596
|
+
These extension methods provide a more ergonomic API compared to explicitly creating encoders/decoders, and work consistently across all types.
|
|
597
|
+
|
|
644
598
|
## Printing JSON
|
|
645
599
|
|
|
646
600
|
### Basic Printing
|
|
@@ -827,7 +781,7 @@ import zio.blocks.schema.json.{Json, JsonPatch}
|
|
|
827
781
|
val source = Json.parseUnsafe("""{"name": "Alice", "age": 30}""")
|
|
828
782
|
val target = Json.parseUnsafe("""{"name": "Alice", "age": 31, "active": true}""")
|
|
829
783
|
|
|
830
|
-
//
|
|
784
|
+
// Create a patch describing the differences
|
|
831
785
|
val patch: JsonPatch = JsonPatch.diff(source, target)
|
|
832
786
|
|
|
833
787
|
// The patch describes the minimal changes:
|
|
@@ -904,11 +858,13 @@ val result = combined(Json.parseUnsafe("""{"x": 1}"""))
|
|
|
904
858
|
`JsonPatch` can be converted to and from `DynamicPatch` for interoperability with the typed patching system:
|
|
905
859
|
|
|
906
860
|
```scala
|
|
907
|
-
import zio.blocks.schema.json.JsonPatch
|
|
861
|
+
import zio.blocks.schema.json.{Json, JsonPatch}
|
|
908
862
|
import zio.blocks.schema.patch.DynamicPatch
|
|
909
863
|
import zio.blocks.schema.SchemaError
|
|
910
864
|
|
|
911
|
-
val
|
|
865
|
+
val source = Json.parseUnsafe("""{"value": 1}""")
|
|
866
|
+
val target = Json.parseUnsafe("""{"value": 2}""")
|
|
867
|
+
val jsonPatch: JsonPatch = JsonPatch.diff(source, target)
|
|
912
868
|
|
|
913
869
|
// Convert to DynamicPatch
|
|
914
870
|
val dynamicPatch: DynamicPatch = jsonPatch.toDynamicPatch
|
|
@@ -951,13 +907,25 @@ val result = json.get("users")(5).get("name").as[String]
|
|
|
951
907
|
### Error Properties
|
|
952
908
|
|
|
953
909
|
```scala
|
|
954
|
-
import zio.blocks.schema.
|
|
955
|
-
import zio.blocks.schema.
|
|
910
|
+
import zio.blocks.schema._
|
|
911
|
+
import zio.blocks.schema.json._
|
|
956
912
|
|
|
957
|
-
|
|
913
|
+
case class User(id: Int, name: String)
|
|
914
|
+
object User {
|
|
915
|
+
implicit val schema: Schema[User] = Schema.derived
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
val codec = User.schema.derive(JsonFormat)
|
|
919
|
+
val invalidJson = """{"id": "not-a-number", "name": "Alice"}"""
|
|
920
|
+
val result = codec.decode(invalidJson)
|
|
958
921
|
|
|
959
|
-
|
|
960
|
-
|
|
922
|
+
// Extract error properties when decoding fails
|
|
923
|
+
result match {
|
|
924
|
+
case Left(error: SchemaError) =>
|
|
925
|
+
error.message // Error description
|
|
926
|
+
error.errors.head.source // DynamicOptic path to error location
|
|
927
|
+
case Right(_) => ()
|
|
928
|
+
}
|
|
961
929
|
```
|
|
962
930
|
|
|
963
931
|
## Cross-Platform Support
|