1515 */
1616package org .springframework .data .rest .webmvc .json ;
1717
18+ import java .io .IOException ;
1819import java .io .InputStream ;
1920import java .util .ArrayList ;
2021import java .util .Arrays ;
2425import java .util .Map ;
2526import java .util .Map .Entry ;
2627import java .util .Optional ;
28+ import java .util .function .IntFunction ;
2729
2830import org .springframework .beans .PropertyAccessor ;
2931import org .springframework .beans .PropertyAccessorFactory ;
4446import org .springframework .http .converter .HttpMessageNotReadableException ;
4547import org .springframework .lang .Nullable ;
4648import org .springframework .util .Assert ;
49+ import org .springframework .util .ClassUtils ;
4750import org .springframework .util .ObjectUtils ;
4851
4952import com .fasterxml .jackson .databind .JsonNode ;
@@ -264,7 +267,10 @@ <T> T doMerge(ObjectNode root, T target, ObjectMapper mapper) throws Exception {
264267
265268 if (child .isArray ()) {
266269
267- if (handleArray (child , it , mapper , property .getTypeInformation ())) {
270+ IntFunction <Object > rawValues = index -> readRawCollectionElement (property .getComponentType (), fieldName ,
271+ index , root , mapper );
272+
273+ if (handleArray (child , it , mapper , property .getTypeInformation (), rawValues )) {
268274 i .remove ();
269275 }
270276
@@ -304,6 +310,16 @@ <T> T doMerge(ObjectNode root, T target, ObjectMapper mapper) throws Exception {
304310 return mapper .readerForUpdating (target ).readValue (root );
305311 }
306312
313+ private static Object readRawCollectionElement (Class <?> elementType , String fieldName , int index , JsonNode root ,
314+ ObjectMapper mapper ) {
315+
316+ try {
317+ return mapper .readerFor (elementType ).at ("/" + fieldName + "/" + index ).readValue (root );
318+ } catch (IOException o_O ) {
319+ throw new RuntimeException (o_O );
320+ }
321+ }
322+
307323 /**
308324 * Handles the given {@link JsonNode} by treating it as {@link ArrayNode} and the given source value as
309325 * {@link Collection}-like value. Looks up the actual type to handle from the potentially available first element,
@@ -316,15 +332,17 @@ <T> T doMerge(ObjectNode root, T target, ObjectMapper mapper) throws Exception {
316332 * @return
317333 * @throws Exception
318334 */
319- private boolean handleArray (JsonNode node , Object source , ObjectMapper mapper , TypeInformation <?> collectionType ) {
335+ private boolean handleArray (JsonNode node , Object source , ObjectMapper mapper , TypeInformation <?> collectionType ,
336+ IntFunction <Object > rawValues ) {
320337
321338 Collection <Object > collection = ifCollection (source );
322339
323340 if (collection == null ) {
324341 return false ;
325342 }
326343
327- return execute (() -> handleArrayNode ((ArrayNode ) node , collection , mapper , collectionType .getComponentType ()));
344+ return execute (
345+ () -> handleArrayNode ((ArrayNode ) node , collection , mapper , collectionType .getComponentType (), rawValues ));
328346 }
329347
330348 /**
@@ -337,27 +355,47 @@ private boolean handleArray(JsonNode node, Object source, ObjectMapper mapper, T
337355 * @return whether an object merge has been applied to the {@link ArrayNode}.
338356 */
339357 private boolean handleArrayNode (ArrayNode array , Collection <Object > collection , ObjectMapper mapper ,
340- TypeInformation <?> componentType ) throws Exception {
358+ TypeInformation <?> componentType , IntFunction < Object > rawValues ) throws Exception {
341359
342360 Assert .notNull (array , "ArrayNode must not be null!" );
343361 Assert .notNull (collection , "Source collection must not be null!" );
344362 Assert .notNull (mapper , "ObjectMapper must not be null!" );
345363
364+ // Empty collection? Primitive? Enum? No need to merge.
365+ if (array .isEmpty ()
366+ || collection .isEmpty ()
367+ || ClassUtils .isPrimitiveOrWrapper (componentType .getType ())
368+ || componentType .getType ().isEnum ()) {
369+ return false ;
370+ }
371+
346372 // We need an iterator for the original collection.
347373 // We might modify it but we want to keep iterating over the original collection.
348374 Iterator <Object > value = new ArrayList <Object >(collection ).iterator ();
349375 boolean nestedObjectFound = false ;
376+ int i = 0 ;
350377
351378 for (JsonNode jsonNode : array ) {
352379
380+ int current = i ++;
381+
382+ // We need to append new elements
353383 if (!value .hasNext ()) {
384+
385+ nestedObjectFound = true ;
386+
387+ // Use pre-read values if available. Deserialize node otherwise.
388+ collection .add (rawValues != null
389+ ? rawValues .apply (current )
390+ : mapper .treeToValue (jsonNode , componentType .getType ()));
391+
354392 break ;
355393 }
356394
357395 Object next = value .next ();
358396
359397 if (ArrayNode .class .isInstance (jsonNode )) {
360- return handleArray (jsonNode , next , mapper , getTypeToMap (value , componentType ));
398+ return handleArray (jsonNode , next , mapper , getTypeToMap (value , componentType ), null );
361399 }
362400
363401 if (ObjectNode .class .isInstance (jsonNode )) {
@@ -410,7 +448,7 @@ private void doMergeNestedMap(Map<Object, Object> source, ObjectNode node, Objec
410448
411449 } else if (value instanceof ArrayNode && sourceValue != null ) {
412450
413- handleArray (value , sourceValue , mapper , getTypeToMap (sourceValue , typeToMap ));
451+ handleArray (value , sourceValue , mapper , getTypeToMap (sourceValue , typeToMap ), null );
414452
415453 } else {
416454
@@ -685,8 +723,8 @@ public void doWithPersistentProperty(PersistentProperty<?> property) {
685723 result = mergeCollections (property , sourceValue , targetValue , mapper );
686724 } else if (property .isEntity ()) {
687725
688- result = targetValue .isPresent () ? targetValue .flatMap (t -> sourceValue .map (s -> mergeForPut (s , t , mapper )))
689- : sourceValue ;
726+ result = targetValue .isPresent () ? targetValue .flatMap (t -> sourceValue .map (s -> mergeForPut (s , t , mapper )))
727+ : sourceValue ;
690728 } else {
691729 result = sourceValue ;
692730 }
0 commit comments