0
# Streaming JSON Input
1
2
Advanced JSON reading and deserialization using `JsonInput` for memory-efficient processing of large JSON documents. Provides fine-grained control over the parsing process, custom type coercers, and streaming access to JSON elements.
3
4
## Capabilities
5
6
### JsonInput Class
7
8
Streaming JSON input processor that implements `Closeable` for resource management.
9
10
```java { .api }
11
/**
12
* The JsonInput class defines the operations used to deserialize JSON strings into Java objects.
13
* Provides streaming access to JSON elements and supports custom type coercion.
14
*/
15
public class JsonInput implements Closeable {
16
17
// Configuration methods
18
19
/**
20
* Change how property setting is done. It's polite to set the value back once done processing.
21
*
22
* @param setter The new PropertySetting to use
23
* @return The previous PropertySetting that has just been replaced
24
*/
25
public PropertySetting propertySetting(PropertySetting setter);
26
27
/**
28
* Add the specified type coercers to the set installed in the JSON coercion manager.
29
*
30
* @param coercers array of zero or more TypeCoercer objects
31
* @return this JsonInput object with added type coercers
32
* @throws JsonException if this JsonInput has already begun processing its input
33
*/
34
public JsonInput addCoercers(TypeCoercer<?>... coercers);
35
36
/**
37
* Add the specified type coercers to the set installed in the JSON coercion manager.
38
*
39
* @param coercers iterable collection of TypeCoercer objects
40
* @return this JsonInput object with added type coercers
41
* @throws JsonException if this JsonInput has already begun processing its input
42
*/
43
public JsonInput addCoercers(Iterable<TypeCoercer<?>> coercers);
44
45
// Element inspection
46
47
/**
48
* Peek at the next input string character to determine the pending JSON element type.
49
*
50
* @return JsonType indicating the pending JSON element type
51
* @throws JsonException if unable to determine the type of the pending element
52
* @throws UncheckedIOException if an I/O exception is encountered
53
*/
54
public JsonType peek();
55
56
// Primitive value reading
57
58
/**
59
* Read the next element of the JSON input stream as a boolean value.
60
*
61
* @return true or false
62
* @throws JsonException if the next element isn't the expected boolean
63
* @throws UncheckedIOException if an I/O exception is encountered
64
*/
65
public boolean nextBoolean();
66
67
/**
68
* Read the next element of the JSON input stream as an object property name.
69
*
70
* @return JSON object property name
71
* @throws JsonException if the next element isn't a string followed by a colon
72
* @throws UncheckedIOException if an I/O exception is encountered
73
*/
74
public String nextName();
75
76
/**
77
* Read the next element of the JSON input stream as a null object.
78
*
79
* @return null object
80
* @throws JsonException if the next element isn't a null
81
* @throws UncheckedIOException if an I/O exception is encountered
82
*/
83
public Object nextNull();
84
85
/**
86
* Read the next element of the JSON input stream as a number.
87
*
88
* @return Number object
89
* @throws JsonException if the next element isn't a number
90
* @throws UncheckedIOException if an I/O exception is encountered
91
*/
92
public Number nextNumber();
93
94
/**
95
* Read the next element of the JSON input stream as a string.
96
*
97
* @return String object
98
* @throws JsonException if the next element isn't a string
99
* @throws UncheckedIOException if an I/O exception is encountered
100
*/
101
public String nextString();
102
103
/**
104
* Read the next element of the JSON input stream as an instant.
105
*
106
* @return Instant object
107
* @throws JsonException if the next element isn't a Long
108
* @throws UncheckedIOException if an I/O exception is encountered
109
*/
110
public Instant nextInstant();
111
112
// Container navigation
113
114
/**
115
* Determine whether an element is pending for the current container from the JSON input stream.
116
*
117
* @return true if an element is pending; otherwise false
118
* @throws JsonException if no container is open
119
* @throws UncheckedIOException if an I/O exception is encountered
120
*/
121
public boolean hasNext();
122
123
/**
124
* Process the opening square bracket of a JSON array.
125
*
126
* @throws UncheckedIOException if an I/O exception is encountered
127
*/
128
public void beginArray();
129
130
/**
131
* Process the closing square bracket of a JSON array.
132
*
133
* @throws UncheckedIOException if an I/O exception is encountered
134
*/
135
public void endArray();
136
137
/**
138
* Process the opening curly brace of a JSON object.
139
*
140
* @throws UncheckedIOException if an I/O exception is encountered
141
*/
142
public void beginObject();
143
144
/**
145
* Process the closing curly brace of a JSON object.
146
*
147
* @throws UncheckedIOException if an I/O exception is encountered
148
*/
149
public void endObject();
150
151
/**
152
* Discard the pending JSON property value.
153
*
154
* @throws JsonException if the pending element isn't a value type
155
* @throws UncheckedIOException if an I/O exception is encountered
156
*/
157
public void skipValue();
158
159
// High-level reading methods
160
161
/**
162
* Read the next element from the JSON input stream as the specified type.
163
*
164
* @param type data type for deserialization (class or TypeToken)
165
* @return object of the specified type deserialized from the JSON input stream
166
* Returns null if the input string is exhausted
167
* @param <T> result type (as specified by type)
168
* @throws JsonException if coercion of the next element to the specified type fails
169
* @throws UncheckedIOException if an I/O exception is encountered
170
*/
171
public <T> T read(Type type);
172
173
/**
174
* Read an array of elements from the JSON input stream with elements as the specified type.
175
*
176
* @param type data type for deserialization (class or TypeToken)
177
* @return list of objects of the specified type deserialized from the JSON input stream
178
* Returns null if the input string is exhausted
179
* @param <T> result type of the item in the list (as specified by type)
180
* @throws JsonException if coercion of the next element to the specified type fails
181
* @throws UncheckedIOException if an I/O exception is encountered
182
*/
183
public <T> List<T> readArray(Type type);
184
185
// Resource management
186
187
/**
188
* Close the input stream.
189
*
190
* @throws UncheckedIOException if an I/O exception is encountered
191
*/
192
public void close();
193
}
194
```
195
196
### JsonType Enum
197
198
Enumeration of JSON element types for parsing control.
199
200
```java { .api }
201
/**
202
* Used to specify the pending JSON element type.
203
*/
204
public enum JsonType {
205
/** Boolean value */
206
BOOLEAN,
207
/** property name */
208
NAME,
209
/** null value */
210
NULL,
211
/** numeric value */
212
NUMBER,
213
/** start of object */
214
START_MAP,
215
/** end of object */
216
END_MAP,
217
/** start of array */
218
START_COLLECTION,
219
/** end of array */
220
END_COLLECTION,
221
/** string value */
222
STRING,
223
/** end of input */
224
END
225
}
226
```
227
228
## Usage Examples
229
230
### Basic Streaming Reading
231
232
```java
233
import org.openqa.selenium.json.Json;
234
import org.openqa.selenium.json.JsonInput;
235
import org.openqa.selenium.json.JsonType;
236
import java.io.StringReader;
237
238
Json json = new Json();
239
String jsonData = "{\"name\":\"John\",\"age\":30,\"active\":true}";
240
241
try (JsonInput input = json.newInput(new StringReader(jsonData))) {
242
input.beginObject();
243
244
while (input.hasNext()) {
245
String propertyName = input.nextName();
246
247
switch (propertyName) {
248
case "name":
249
String name = input.nextString();
250
System.out.println("Name: " + name);
251
break;
252
case "age":
253
Number age = input.nextNumber();
254
System.out.println("Age: " + age);
255
break;
256
case "active":
257
boolean active = input.nextBoolean();
258
System.out.println("Active: " + active);
259
break;
260
default:
261
input.skipValue(); // Skip unknown properties
262
}
263
}
264
265
input.endObject();
266
}
267
```
268
269
### Array Processing
270
271
```java
272
String jsonArray = "[\"apple\",\"banana\",\"cherry\"]";
273
274
try (JsonInput input = json.newInput(new StringReader(jsonArray))) {
275
List<String> fruits = new ArrayList<>();
276
277
input.beginArray();
278
while (input.hasNext()) {
279
fruits.add(input.nextString());
280
}
281
input.endArray();
282
283
System.out.println("Fruits: " + fruits);
284
}
285
286
// Or use the convenient readArray method
287
try (JsonInput input = json.newInput(new StringReader(jsonArray))) {
288
List<String> fruits = input.readArray(String.class);
289
System.out.println("Fruits: " + fruits);
290
}
291
```
292
293
### Type Inspection with Peek
294
295
```java
296
String jsonData = "{\"count\":42,\"message\":\"hello\",\"valid\":true}";
297
298
try (JsonInput input = json.newInput(new StringReader(jsonData))) {
299
input.beginObject();
300
301
while (input.hasNext()) {
302
String propertyName = input.nextName();
303
JsonType valueType = input.peek();
304
305
switch (valueType) {
306
case NUMBER:
307
Number num = input.nextNumber();
308
System.out.println(propertyName + " (number): " + num);
309
break;
310
case STRING:
311
String str = input.nextString();
312
System.out.println(propertyName + " (string): " + str);
313
break;
314
case BOOLEAN:
315
boolean bool = input.nextBoolean();
316
System.out.println(propertyName + " (boolean): " + bool);
317
break;
318
case NULL:
319
input.nextNull();
320
System.out.println(propertyName + " (null)");
321
break;
322
default:
323
input.skipValue();
324
System.out.println(propertyName + " (skipped)");
325
}
326
}
327
328
input.endObject();
329
}
330
```
331
332
### Custom Type Coercers
333
334
```java
335
import org.openqa.selenium.json.TypeCoercer;
336
import org.openqa.selenium.json.PropertySetting;
337
import java.lang.reflect.Type;
338
import java.util.function.BiFunction;
339
340
// Custom coercer for LocalDate
341
public class LocalDateCoercer extends TypeCoercer<LocalDate> {
342
@Override
343
public boolean test(Class<?> aClass) {
344
return LocalDate.class.isAssignableFrom(aClass);
345
}
346
347
@Override
348
public BiFunction<JsonInput, PropertySetting, LocalDate> apply(Type type) {
349
return (jsonInput, propertySetting) -> {
350
String dateStr = jsonInput.nextString();
351
return LocalDate.parse(dateStr);
352
};
353
}
354
}
355
356
// Usage
357
String jsonData = "{\"date\":\"2023-12-25\"}";
358
359
try (JsonInput input = json.newInput(new StringReader(jsonData))) {
360
input.addCoercers(new LocalDateCoercer());
361
362
input.beginObject();
363
input.nextName(); // "date"
364
LocalDate date = input.read(LocalDate.class);
365
input.endObject();
366
367
System.out.println("Date: " + date);
368
}
369
```
370
371
### Property Setting Strategies
372
373
```java
374
// Switch between property setting strategies
375
try (JsonInput input = json.newInput(new StringReader(jsonData))) {
376
PropertySetting original = input.propertySetting(PropertySetting.BY_FIELD);
377
378
// Read objects using direct field access
379
MyObject obj = input.read(MyObject.class);
380
381
// Restore original setting
382
input.propertySetting(original);
383
}
384
```
385
386
### Nested Object Processing
387
388
```java
389
String nestedJson = """
390
{
391
"user": {
392
"name": "John",
393
"preferences": {
394
"theme": "dark",
395
"notifications": true
396
}
397
},
398
"lastLogin": "2023-12-25T10:30:00Z"
399
}
400
""";
401
402
try (JsonInput input = json.newInput(new StringReader(nestedJson))) {
403
input.beginObject();
404
405
while (input.hasNext()) {
406
String key = input.nextName();
407
408
if ("user".equals(key)) {
409
input.beginObject();
410
411
while (input.hasNext()) {
412
String userKey = input.nextName();
413
414
if ("name".equals(userKey)) {
415
String name = input.nextString();
416
System.out.println("User name: " + name);
417
} else if ("preferences".equals(userKey)) {
418
input.beginObject();
419
420
while (input.hasNext()) {
421
String prefKey = input.nextName();
422
423
if ("theme".equals(prefKey)) {
424
String theme = input.nextString();
425
System.out.println("Theme: " + theme);
426
} else if ("notifications".equals(prefKey)) {
427
boolean notifications = input.nextBoolean();
428
System.out.println("Notifications: " + notifications);
429
} else {
430
input.skipValue();
431
}
432
}
433
434
input.endObject();
435
} else {
436
input.skipValue();
437
}
438
}
439
440
input.endObject();
441
} else if ("lastLogin".equals(key)) {
442
Instant lastLogin = input.nextInstant();
443
System.out.println("Last login: " + lastLogin);
444
} else {
445
input.skipValue();
446
}
447
}
448
449
input.endObject();
450
}
451
```