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,11 @@ <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 , index ,
271+ root ,
272+ mapper );
273+
274+ if (handleArray (child , it , mapper , property .getTypeInformation (), rawValues )) {
268275 i .remove ();
269276 }
270277
@@ -304,6 +311,16 @@ <T> T doMerge(ObjectNode root, T target, ObjectMapper mapper) throws Exception {
304311 return mapper .readerForUpdating (target ).readValue (root );
305312 }
306313
314+ private static Object readRawCollectionElement (Class <?> elementType , String fieldName , int index , JsonNode root ,
315+ ObjectMapper mapper ) {
316+
317+ try {
318+ return mapper .readerFor (elementType ).at ("/" + fieldName + "/" + index ).readValue (root );
319+ } catch (IOException o_O ) {
320+ throw new RuntimeException (o_O );
321+ }
322+ }
323+
307324 /**
308325 * Handles the given {@link JsonNode} by treating it as {@link ArrayNode} and the given source value as
309326 * {@link Collection}-like value. Looks up the actual type to handle from the potentially available first element,
@@ -316,15 +333,17 @@ <T> T doMerge(ObjectNode root, T target, ObjectMapper mapper) throws Exception {
316333 * @return
317334 * @throws Exception
318335 */
319- private boolean handleArray (JsonNode node , Object source , ObjectMapper mapper , TypeInformation <?> collectionType ) {
336+ private boolean handleArray (JsonNode node , Object source , ObjectMapper mapper , TypeInformation <?> collectionType ,
337+ IntFunction <Object > rawValues ) {
320338
321339 Collection <Object > collection = ifCollection (source );
322340
323341 if (collection == null ) {
324342 return false ;
325343 }
326344
327- return execute (() -> handleArrayNode ((ArrayNode ) node , collection , mapper , collectionType .getComponentType ()));
345+ return execute (
346+ () -> handleArrayNode ((ArrayNode ) node , collection , mapper , collectionType .getComponentType (), rawValues ));
328347 }
329348
330349 /**
@@ -337,27 +356,47 @@ private boolean handleArray(JsonNode node, Object source, ObjectMapper mapper, T
337356 * @return whether an object merge has been applied to the {@link ArrayNode}.
338357 */
339358 private boolean handleArrayNode (ArrayNode array , Collection <Object > collection , ObjectMapper mapper ,
340- TypeInformation <?> componentType ) throws Exception {
359+ TypeInformation <?> componentType , IntFunction < Object > rawValues ) throws Exception {
341360
342361 Assert .notNull (array , "ArrayNode must not be null" );
343362 Assert .notNull (collection , "Source collection must not be null" );
344363 Assert .notNull (mapper , "ObjectMapper must not be null" );
345364
365+ // Empty collection? Primitive? Enum? No need to merge.
366+ if (array .isEmpty ()
367+ || collection .isEmpty ()
368+ || ClassUtils .isPrimitiveOrWrapper (componentType .getType ())
369+ || componentType .getType ().isEnum ()) {
370+ return false ;
371+ }
372+
346373 // We need an iterator for the original collection.
347374 // We might modify it but we want to keep iterating over the original collection.
348375 Iterator <Object > value = new ArrayList <Object >(collection ).iterator ();
349376 boolean nestedObjectFound = false ;
377+ int i = 0 ;
350378
351379 for (JsonNode jsonNode : array ) {
352380
381+ int current = i ++;
382+
383+ // We need to append new elements
353384 if (!value .hasNext ()) {
385+
386+ nestedObjectFound = true ;
387+
388+ // Use pre-read values if available. Deserialize node otherwise.
389+ collection .add (rawValues != null
390+ ? rawValues .apply (current )
391+ : mapper .treeToValue (jsonNode , componentType .getType ()));
392+
354393 break ;
355394 }
356395
357396 Object next = value .next ();
358397
359398 if (ArrayNode .class .isInstance (jsonNode )) {
360- return handleArray (jsonNode , next , mapper , getTypeToMap (value , componentType ));
399+ return handleArray (jsonNode , next , mapper , getTypeToMap (value , componentType ), null );
361400 }
362401
363402 if (ObjectNode .class .isInstance (jsonNode )) {
@@ -410,7 +449,7 @@ private void doMergeNestedMap(Map<Object, Object> source, ObjectNode node, Objec
410449
411450 } else if (value instanceof ArrayNode && sourceValue != null ) {
412451
413- handleArray (value , sourceValue , mapper , getTypeToMap (sourceValue , typeToMap ));
452+ handleArray (value , sourceValue , mapper , getTypeToMap (sourceValue , typeToMap ), null );
414453
415454 } else {
416455
0 commit comments