|
1 | 1 | /*
|
2 |
| - * Copyright 2014-2016 the original author or authors. |
| 2 | + * Copyright 2014-2017 the original author or authors. |
3 | 3 | *
|
4 | 4 | * Licensed under the Apache License, Version 2.0 (the "License");
|
5 | 5 | * you may not use this file except in compliance with the License.
|
|
21 | 21 | import java.io.InputStream;
|
22 | 22 | import java.util.ArrayList;
|
23 | 23 | import java.util.Arrays;
|
24 |
| -import java.util.HashMap; |
25 | 24 | import java.util.Collection;
|
| 25 | +import java.util.HashMap; |
26 | 26 | import java.util.Iterator;
|
27 | 27 | import java.util.Map;
|
28 | 28 | import java.util.Map.Entry;
|
|
42 | 42 | import com.fasterxml.jackson.databind.JsonNode;
|
43 | 43 | import com.fasterxml.jackson.databind.ObjectMapper;
|
44 | 44 | import com.fasterxml.jackson.databind.introspect.BasicClassIntrospector;
|
45 |
| -import com.fasterxml.jackson.databind.node.ArrayNode; |
46 | 45 | import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
|
47 | 46 | import com.fasterxml.jackson.databind.introspect.ClassIntrospector;
|
| 47 | +import com.fasterxml.jackson.databind.node.ArrayNode; |
48 | 48 | import com.fasterxml.jackson.databind.node.ObjectNode;
|
49 | 49 |
|
50 | 50 | /**
|
@@ -208,7 +208,7 @@ private <T> T doMerge(ObjectNode root, T target, ObjectMapper mapper) throws Exc
|
208 | 208 | continue;
|
209 | 209 | }
|
210 | 210 |
|
211 |
| - doMergeNestedMap((Map<String, Object>) rawValue, objectNode, mapper, property.getTypeInformation()); |
| 211 | + doMergeNestedMap((Map<Object, Object>) rawValue, objectNode, mapper, property.getTypeInformation()); |
212 | 212 |
|
213 | 213 | // Remove potentially emptied Map as values have been handled recursively
|
214 | 214 | if (!objectNode.fieldNames().hasNext()) {
|
@@ -317,33 +317,38 @@ private boolean handleArrayNode(ArrayNode array, Collection<Object> collection,
|
317 | 317 | * @param mapper must not be {@literal null}.
|
318 | 318 | * @throws Exception
|
319 | 319 | */
|
320 |
| - private void doMergeNestedMap(Map<String, Object> source, ObjectNode node, ObjectMapper mapper, |
| 320 | + private void doMergeNestedMap(Map<Object, Object> source, ObjectNode node, ObjectMapper mapper, |
321 | 321 | TypeInformation<?> type) throws Exception {
|
322 | 322 |
|
323 | 323 | if (source == null) {
|
324 | 324 | return;
|
325 | 325 | }
|
326 | 326 |
|
327 | 327 | Iterator<Entry<String, JsonNode>> fields = node.fields();
|
| 328 | + Class<?> keyType = typeOrObject(type.getComponentType()); |
| 329 | + Class<?> valueType = typeOrObject(type.getMapValueType()); |
328 | 330 |
|
329 | 331 | while (fields.hasNext()) {
|
330 | 332 |
|
331 | 333 | Entry<String, JsonNode> entry = fields.next();
|
332 |
| - JsonNode child = entry.getValue(); |
333 |
| - Object sourceValue = source.get(entry.getKey()); |
| 334 | + JsonNode value = entry.getValue(); |
| 335 | + String key = entry.getKey(); |
334 | 336 |
|
335 |
| - if (child instanceof ObjectNode && sourceValue != null) { |
| 337 | + Object mappedKey = mapper.readValue(quote(key), keyType); |
| 338 | + Object sourceValue = source.get(mappedKey); |
336 | 339 |
|
337 |
| - doMerge((ObjectNode) child, sourceValue, mapper); |
| 340 | + if (value instanceof ObjectNode && sourceValue != null) { |
338 | 341 |
|
339 |
| - } else if (child instanceof ArrayNode && sourceValue != null) { |
| 342 | + doMerge((ObjectNode) value, sourceValue, mapper); |
340 | 343 |
|
341 |
| - handleArray(child, sourceValue, mapper, type); |
| 344 | + } else if (value instanceof ArrayNode && sourceValue != null) { |
| 345 | + |
| 346 | + handleArray(value, sourceValue, mapper, type); |
342 | 347 |
|
343 | 348 | } else {
|
344 | 349 |
|
345 |
| - Class<?> valueType = sourceValue == null ? Object.class : sourceValue.getClass(); |
346 |
| - source.put(entry.getKey(), mapper.treeToValue(child, valueType)); |
| 350 | + Class<?> typeToRead = sourceValue != null ? sourceValue.getClass() : valueType; |
| 351 | + source.put(mappedKey, mapper.treeToValue(value, typeToRead)); |
347 | 352 | }
|
348 | 353 |
|
349 | 354 | fields.remove();
|
@@ -374,13 +379,30 @@ private static Collection<Object> ifCollection(Object source) {
|
374 | 379 | }
|
375 | 380 |
|
376 | 381 | /**
|
377 |
| - * Simple value object to capture a mapping of Jackson mapped field names and {@link PersistentProperty} instances. |
| 382 | + * Surrounds the given source {@link String} with quotes so that they represent a valid JSON String. |
378 | 383 | *
|
379 | 384 | * @param source can be {@literal null}.
|
| 385 | + * @return |
| 386 | + */ |
| 387 | + private static String quote(String source) { |
| 388 | + return source == null ? null : "\"".concat(source).concat("\""); |
| 389 | + } |
| 390 | + |
| 391 | + /** |
| 392 | + * Returns the raw type of the given {@link TypeInformation} or {@link Object} as fallback. |
| 393 | + * |
| 394 | + * @param type can be {@literal null}. |
| 395 | + * @return |
| 396 | + */ |
| 397 | + private static Class<?> typeOrObject(TypeInformation<?> type) { |
| 398 | + return type == null ? Object.class : type.getType(); |
| 399 | + } |
| 400 | + |
| 401 | + /** |
| 402 | + * Simple value object to capture a mapping of Jackson mapped field names and {@link PersistentProperty} instances. |
380 | 403 | *
|
381 | 404 | * @author Oliver Gierke
|
382 | 405 | */
|
383 |
| - @SuppressWarnings("unchecked") |
384 | 406 | static class MappedProperties {
|
385 | 407 |
|
386 | 408 | private static final ClassIntrospector INTROSPECTOR = new BasicClassIntrospector();
|
|
0 commit comments