29
29
import java .util .Objects ;
30
30
import java .util .Optional ;
31
31
import java .util .ServiceLoader ;
32
+ import java .util .concurrent .atomic .AtomicReference ;
32
33
import java .util .function .Predicate ;
33
34
import java .util .stream .Collectors ;
34
35
import java .util .stream .Stream ;
@@ -223,36 +224,60 @@ private static boolean isEmpty(final Iterable<?> iterable) {
223
224
@ Override
224
225
public boolean contains (final CharSequence key ) {
225
226
requireNonNull (key , "The key or pointer to check the existence of a value for must not be null!" );
227
+ return internalContains (key , false );
228
+ }
229
+
230
+ @ Override
231
+ public boolean containsFlatteningArrays (final CharSequence key ) {
232
+ requireNonNull (key , "The key or pointer to check the existence of a value for must not be null!" );
233
+ return internalContains (key , true );
234
+ }
226
235
236
+ boolean internalContains (final CharSequence key , final boolean flatteningArrays ) {
227
237
final boolean result ;
228
238
229
239
final JsonPointer pointer = JsonPointer .of (key );
230
240
231
241
if (1 >= pointer .getLevelCount ()) {
232
242
result = pointer .getRoot ().map (this ::containsKey ).orElse (false );
233
243
} else {
234
- result = pointer .getRoot ()
235
- .flatMap (this ::getValueForKey )
236
- .filter (JsonValue ::isObject )
237
- .map (JsonValue ::asObject )
238
- .map (jsonObject -> jsonObject .contains (pointer .nextLevel ()))
239
- .orElse (false );
244
+ result = containsPointer (pointer , flatteningArrays );
240
245
}
241
246
242
247
return result ;
243
248
}
244
249
250
+ private Boolean containsPointer (final JsonPointer pointer , final boolean flatteningArrays ) {
251
+ return pointer .getRoot ()
252
+ .flatMap (this ::getValueForKey )
253
+ .filter (val -> val .isObject () || (flatteningArrays && val .isArray ()))
254
+ .map (val -> val .isObject () ? Stream .of (val .asObject ()) :
255
+ val .asArray ().stream ().filter (JsonValue ::isObject ).map (JsonValue ::asObject )
256
+ )
257
+ .map (stream -> stream .anyMatch (jsonObject -> flatteningArrays ?
258
+ jsonObject .containsFlatteningArrays (pointer .nextLevel ()) :
259
+ jsonObject .contains (pointer .nextLevel ()))
260
+ )
261
+ .orElse (false );
262
+ }
263
+
245
264
private boolean containsKey (final CharSequence key ) {
246
265
return fieldMap .containsKey (key .toString ());
247
266
}
248
267
249
268
@ Override
250
269
public Optional <JsonValue > getValue (final CharSequence key ) {
251
270
requireNonNull (key , "The key or pointer of the value to be retrieved must not be null!" );
252
- return getValueForPointer (JsonPointer .of (key ));
271
+ return getValueForPointer (JsonPointer .of (key ), false );
272
+ }
273
+
274
+ @ Override
275
+ public Optional <JsonValue > getValueFlatteningArrays (final CharSequence key ) {
276
+ requireNonNull (key , "The key or pointer of the value to be retrieved must not be null!" );
277
+ return getValueForPointer (JsonPointer .of (key ), true );
253
278
}
254
279
255
- private Optional <JsonValue > getValueForPointer (final JsonPointer pointer ) {
280
+ private Optional <JsonValue > getValueForPointer (final JsonPointer pointer , final boolean flatteningArrays ) {
256
281
final Optional <JsonValue > result ;
257
282
258
283
final JsonKey rootKey = pointer .getRoot ().orElse (ROOT_KEY );
@@ -263,10 +288,31 @@ private Optional<JsonValue> getValueForPointer(final JsonPointer pointer) {
263
288
// same as getting a value for a key
264
289
result = getValueForKey (rootKey );
265
290
} else {
266
- result = getValueForKey (rootKey )
267
- .filter (JsonValue ::isObject )
268
- .map (JsonValue ::asObject )
269
- .flatMap (jsonObject -> jsonObject .getValue (pointer .nextLevel ()));
291
+ final AtomicReference <Boolean > valueIsArray = new AtomicReference <>(false );
292
+ final List <JsonValue > collected = getValueForKey (rootKey ).map (Stream ::of ).orElse (Stream .empty ())
293
+ .filter (val -> val .isObject () || (flatteningArrays && val .isArray ()))
294
+ .flatMap (val -> {
295
+ if (val .isObject ()) {
296
+ return Stream .of (val .asObject ());
297
+ } else {
298
+ valueIsArray .set (true );
299
+ return val .asArray ().stream ().filter (JsonValue ::isObject ).map (JsonValue ::asObject );
300
+ }
301
+ })
302
+ .flatMap (jsonObject -> flatteningArrays ?
303
+ jsonObject .getValueFlatteningArrays (pointer .nextLevel ())
304
+ .map (Stream ::of ).orElseGet (Stream ::empty ) :
305
+ jsonObject .getValue (pointer .nextLevel ())
306
+ .map (Stream ::of ).orElseGet (Stream ::empty )
307
+ ).collect (Collectors .toList ());
308
+
309
+ if (collected .isEmpty ()) {
310
+ result = Optional .empty ();
311
+ } else if (Boolean .TRUE .equals (valueIsArray .get ())) {
312
+ result = Optional .of (collected .stream ().collect (JsonCollectors .valuesToArray ()));
313
+ } else {
314
+ result = Optional .of (collected .get (0 ));
315
+ }
270
316
}
271
317
272
318
return result ;
@@ -281,7 +327,7 @@ private Optional<JsonValue> getValueForKey(final CharSequence key) {
281
327
public <T > Optional <T > getValue (final JsonFieldDefinition <T > fieldDefinition ) {
282
328
checkFieldDefinition (fieldDefinition );
283
329
284
- return getValueForPointer (fieldDefinition .getPointer ()).map (fieldDefinition ::mapValue );
330
+ return getValueForPointer (fieldDefinition .getPointer (), false ).map (fieldDefinition ::mapValue );
285
331
}
286
332
287
333
private static void checkFieldDefinition (final JsonFieldDefinition <?> fieldDefinition ) {
@@ -308,7 +354,7 @@ public JsonObject get(final JsonPointer pointer) {
308
354
final Optional <JsonFieldDefinition > rootKeyDefinition = getDefinitionForKey (rootKey );
309
355
if (1 >= pointer .getLevelCount ()) {
310
356
result = rootKeyValue .map (
311
- jsonValue -> JsonField .newInstance (rootKey , jsonValue , rootKeyDefinition .orElse (null )))
357
+ jsonValue -> JsonField .newInstance (rootKey , jsonValue , rootKeyDefinition .orElse (null )))
312
358
.map (jsonField -> Collections .singletonMap (jsonField .getKeyName (), jsonField ))
313
359
.map (ImmutableJsonObject ::of )
314
360
.orElseGet (ImmutableJsonObject ::empty );
@@ -321,16 +367,16 @@ public JsonObject get(final JsonPointer pointer) {
321
367
.isPresent ();
322
368
323
369
result = rootKeyValue .map (jsonValue -> {
324
- if (jsonValue .isObject ()) {
325
- if (containsNextLevelRootKey .test (jsonValue .asObject ())) {
326
- return jsonValue .asObject ().get (nextPointerLevel ); // Recursion
327
- } else {
328
- return null ;
329
- }
330
- } else {
331
- return jsonValue ;
332
- }
333
- })
370
+ if (jsonValue .isObject ()) {
371
+ if (containsNextLevelRootKey .test (jsonValue .asObject ())) {
372
+ return jsonValue .asObject ().get (nextPointerLevel ); // Recursion
373
+ } else {
374
+ return null ;
375
+ }
376
+ } else {
377
+ return jsonValue ;
378
+ }
379
+ })
334
380
.map (jsonValue -> JsonField .newInstance (rootKey , jsonValue , rootKeyDefinition .orElse (null )))
335
381
.map (jsonField -> Collections .singletonMap (jsonField .getKeyName (), jsonField ))
336
382
.map (ImmutableJsonObject ::of )
@@ -360,7 +406,7 @@ public JsonObject get(final JsonFieldSelector fieldSelector) {
360
406
361
407
final List <JsonPointer > pointersContainedInThis = fieldSelector .getPointers ()
362
408
.stream ()
363
- .filter (this ::contains )
409
+ .filter (this ::containsFlatteningArrays )
364
410
.collect (Collectors .toList ());
365
411
366
412
if (pointersContainedInThis .isEmpty ()) {
@@ -381,9 +427,17 @@ private static JsonObject filterByTrie(final JsonObject self, final JsonFieldSel
381
427
for (final JsonKey key : trie .getKeys ()) {
382
428
self .getField (key ).ifPresent (child -> {
383
429
final JsonValue childValue = child .getValue ();
384
- final JsonValue filteredChildValue = childValue .isObject ()
385
- ? filterByTrie (childValue .asObject (), trie .descend (key ))
386
- : childValue ;
430
+ final JsonValue filteredChildValue ;
431
+ if (childValue .isObject ()) {
432
+ filteredChildValue = filterByTrie (childValue .asObject (), trie .descend (key )); // recurse!
433
+ } else if (childValue .isArray ()) {
434
+ filteredChildValue = childValue .asArray ().stream ()
435
+ .filter (JsonValue ::isObject )
436
+ .map (value -> filterByTrie (value .asObject (), trie .descend (key ))) // recurse!
437
+ .collect (JsonCollectors .valuesToArray ());
438
+ } else {
439
+ filteredChildValue = childValue ;
440
+ }
387
441
final Optional <JsonFieldDefinition > childFieldDefinition = child .getDefinition ();
388
442
if (childFieldDefinition .isPresent ()) {
389
443
builder .set (childFieldDefinition .get (), filteredChildValue );
@@ -422,7 +476,8 @@ private JsonObject removeForPointer(final JsonPointer pointer) {
422
476
.map (JsonValue ::asObject )
423
477
.filter (containsNextLevelRootKey )
424
478
.map (jsonObject -> jsonObject .remove (nextPointerLevel )) // Recursion
425
- .map (withoutValue -> JsonField .newInstance (rootKey , withoutValue , getDefinitionForKey (rootKey ).orElse (null )))
479
+ .map (withoutValue -> JsonField .newInstance (rootKey , withoutValue ,
480
+ getDefinitionForKey (rootKey ).orElse (null )))
426
481
.map (this ::set )
427
482
.orElse (this );
428
483
}
@@ -568,7 +623,7 @@ private SoftReferencedFieldMap(final Map<String, JsonField> jsonFieldMap,
568
623
if (CBOR_FACTORY .isCborAvailable ()) {
569
624
try {
570
625
this .cborObjectRepresentation = CBOR_FACTORY .createCborRepresentation (jsonFieldMap ,
571
- guessSerializedSize ());
626
+ guessSerializedSize ());
572
627
} catch (final IOException e ) {
573
628
assert false ; // this should not happen, so assertions will throw during testing
574
629
jsonObjectStringRepresentation = createStringRepresentation (jsonFieldMap );
0 commit comments