Skip to content

Commit 45ab9ab

Browse files
author
Mathias Düsterhöft
committed
DATAREST-956 handle add and remove item to nested collections
1 parent a1c538c commit 45ab9ab

File tree

2 files changed

+71
-6
lines changed

2 files changed

+71
-6
lines changed

spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/DomainObjectReader.java

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
* @author Oliver Gierke
4949
* @author Mark Paluch
5050
* @author Craig Andrews
51+
* @author Mathias Düsterhöft
5152
* @since 2.2
5253
*/
5354
@RequiredArgsConstructor
@@ -175,7 +176,7 @@ private <T> T doMerge(ObjectNode root, T target, ObjectMapper mapper) throws Exc
175176

176177
if (child.isArray()) {
177178

178-
boolean nestedObjectFound = handleArrayNode((ArrayNode) child, asCollection(rawValue), mapper);
179+
boolean nestedObjectFound = handleArrayNode((ArrayNode) child, asCollection(rawValue), property.getComponentType(), mapper);
179180

180181
if (nestedObjectFound) {
181182
i.remove();
@@ -224,10 +225,10 @@ private <T> T doMerge(ObjectNode root, T target, ObjectMapper mapper) throws Exc
224225
*
225226
* @param array the source {@link ArrayNode}m, must not be {@literal null}.
226227
* @param collection the actual collection values, must not be {@literal null}.
227-
* @param mapper the {@link ObjectMapper} to use, must not be {@literal null}.
228-
* @return whether an object merge has been applied to the {@link ArrayNode}.
228+
* @param componentType the item type of the collection
229+
* @param mapper the {@link ObjectMapper} to use, must not be {@literal null}. @return whether an object merge has been applied to the {@link ArrayNode}.
229230
*/
230-
private boolean handleArrayNode(ArrayNode array, Collection<Object> collection, ObjectMapper mapper)
231+
private boolean handleArrayNode(ArrayNode array, Collection<Object> collection, Class<?> componentType, ObjectMapper mapper)
231232
throws Exception {
232233

233234
Assert.notNull(array, "ArrayNode must not be null!");
@@ -240,13 +241,18 @@ private boolean handleArrayNode(ArrayNode array, Collection<Object> collection,
240241
for (JsonNode jsonNode : array) {
241242

242243
if (!value.hasNext()) {
244+
if (componentType != null) {
245+
collection.add(mapper.treeToValue(jsonNode, componentType));
246+
}
243247
return nestedObjectFound;
244248
}
245249

246250
Object next = value.next();
247251

248252
if (ArrayNode.class.isInstance(jsonNode)) {
249-
return handleArrayNode(array, asCollection(next), mapper);
253+
final Collection<Object> nestedCollection = asCollection(next);
254+
final Iterator<Object> iterator = nestedCollection.iterator();
255+
return handleArrayNode(array, nestedCollection, iterator.hasNext() ? iterator.next().getClass() : null, mapper);
250256
}
251257

252258
if (ObjectNode.class.isInstance(jsonNode)) {
@@ -256,6 +262,12 @@ private boolean handleArrayNode(ArrayNode array, Collection<Object> collection,
256262
}
257263
}
258264

265+
while (value.hasNext()) {
266+
//there are more items in the collection than contained in the json node - remove it.
267+
value.next();
268+
value.remove();
269+
}
270+
259271
return nestedObjectFound;
260272
}
261273

@@ -284,7 +296,9 @@ private void doMergeNestedMap(Map<String, Object> source, ObjectNode node, Objec
284296
if (child instanceof ObjectNode && sourceValue != null) {
285297
doMerge((ObjectNode) child, sourceValue, mapper);
286298
} else if (child instanceof ArrayNode && sourceValue != null) {
287-
handleArrayNode((ArrayNode) child, asCollection(sourceValue), mapper);
299+
final Collection<Object> nestedCollection = asCollection(sourceValue);
300+
final Iterator<Object> iterator = nestedCollection.iterator();
301+
handleArrayNode((ArrayNode) child, nestedCollection, iterator.hasNext() ? iterator.next().getClass() : null, mapper);
288302
} else {
289303
source.put(entry.getKey(),
290304
mapper.treeToValue(child, sourceValue == null ? Object.class : sourceValue.getClass()));

spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/DomainObjectReaderUnitTests.java

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
import static org.junit.Assert.*;
2020
import static org.mockito.Mockito.*;
2121

22+
import lombok.AllArgsConstructor;
23+
import lombok.NoArgsConstructor;
24+
2225
import java.io.ByteArrayInputStream;
2326
import java.util.ArrayList;
2427
import java.util.Arrays;
@@ -60,6 +63,7 @@
6063
*
6164
* @author Oliver Gierke
6265
* @author Craig Andrews
66+
* @author Mathias Düsterhöft
6367
*/
6468
@RunWith(MockitoJUnitRunner.class)
6569
public class DomainObjectReaderUnitTests {
@@ -373,6 +377,51 @@ public void writesArrayForPut() throws Exception {
373377
assertThat(result.inner.items.get(0).some, is("value"));
374378
}
375379

380+
/**
381+
* @see DATAREST-956
382+
*/
383+
@Test
384+
public void writesArrayWithAddedItemForPut() throws Exception {
385+
386+
Child inner = new Child();
387+
inner.items = new ArrayList<Item>();
388+
inner.items.add(new Item());
389+
390+
Parent source = new Parent();
391+
source.inner = inner;
392+
393+
JsonNode node = new ObjectMapper().readTree("{ \"inner\" : { \"items\" : [ { \"some\" : \"value\" }, { \"some\" : \"value1\" } ] } }");
394+
395+
Parent result = reader.readPut((ObjectNode) node, source, new ObjectMapper());
396+
397+
assertThat(result.inner.items.size(), is(2));
398+
assertThat(result.inner.items.get(0).some, is("value"));
399+
assertThat(result.inner.items.get(1).some, is("value1"));
400+
}
401+
402+
/**
403+
* @see DATAREST-956
404+
*/
405+
@Test
406+
public void writesArrayWithRemovedItemForPut() throws Exception {
407+
408+
Child inner = new Child();
409+
inner.items = new ArrayList<Item>();
410+
inner.items.add(new Item("test1"));
411+
inner.items.add(new Item("test2"));
412+
inner.items.add(new Item("test3"));
413+
414+
Parent source = new Parent();
415+
source.inner = inner;
416+
417+
JsonNode node = new ObjectMapper().readTree("{ \"inner\" : { \"items\" : [ { \"some\" : \"value\" } ] } }");
418+
419+
Parent result = reader.readPut((ObjectNode) node, source, new ObjectMapper());
420+
421+
assertThat(result.inner.items.size(), is(1));
422+
assertThat(result.inner.items.get(0).some, is("value"));
423+
}
424+
376425
@JsonAutoDetect(fieldVisibility = Visibility.ANY)
377426
static class TypeWithGenericMap {
378427

@@ -440,6 +489,8 @@ static class Child {
440489
}
441490

442491
@JsonAutoDetect(fieldVisibility = Visibility.ANY)
492+
@NoArgsConstructor
493+
@AllArgsConstructor
443494
static class Item {
444495
String some;
445496
}

0 commit comments

Comments
 (0)