Skip to content

Commit

Permalink
Fix #2733
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed May 22, 2020
1 parent 729d013 commit 2708892
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 12 deletions.
1 change: 1 addition & 0 deletions release-notes/VERSION-2.x
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Project: jackson-databind
an explicit name
(reported, fix contributed by David B)
#2732: Allow `JsonNode` auto-convert into `ArrayNode` if duplicates found (for XML)
#2733: Allow values of "untyped" auto-convert into `List` if duplicates found (for XML)
- Add `BeanDeserializerBase.isCaseInsensitive()`
- Some refactoring of `CollectionDeserializer` to solve CSV array handling issues

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,9 +197,11 @@ public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
&& getClass() == UntypedObjectDeserializer.class) {
return Vanilla.instance(preventMerge);
}

if (preventMerge != _nonMerging) {
return new UntypedObjectDeserializer(this, preventMerge);
}

return this;
}

Expand Down Expand Up @@ -501,44 +503,92 @@ protected Object mapObject(JsonParser p, DeserializationContext ctxt) throws IOE
}
if (key1 == null) {
// empty map might work; but caller may want to modify... so better just give small modifiable
return new LinkedHashMap<String,Object>(2);
return new LinkedHashMap<>(2);
}
// minor optimization; let's handle 1 and 2 entry cases separately
// 24-Mar-2015, tatu: Ideally, could use one of 'nextXxx()' methods, but for
// that we'd need new method(s) in JsonDeserializer. So not quite yet.
p.nextToken();
Object value1 = deserialize(p, ctxt);

String key2 = p.nextFieldName();
if (key2 == null) { // has to be END_OBJECT, then
// single entry; but we want modifiable
LinkedHashMap<String, Object> result = new LinkedHashMap<String, Object>(2);
LinkedHashMap<String, Object> result = new LinkedHashMap<>(2);
result.put(key1, value1);
return result;
}
p.nextToken();
Object value2 = deserialize(p, ctxt);

String key = p.nextFieldName();

if (key == null) {
LinkedHashMap<String, Object> result = new LinkedHashMap<String, Object>(4);
LinkedHashMap<String, Object> result = new LinkedHashMap<>(4);
result.put(key1, value1);
result.put(key2, value2);
if (result.put(key2, value2) != null) {
// 22-May-2020, tatu: [databind#2733] may need extra handling
return _mapObjectWithDups(p, ctxt, result, key1, value1, value2, key);
}
return result;
}
// And then the general case; default map size is 16
LinkedHashMap<String, Object> result = new LinkedHashMap<String, Object>();
LinkedHashMap<String, Object> result = new LinkedHashMap<>();
result.put(key1, value1);
result.put(key2, value2);
if (result.put(key2, value2) != null) {
// 22-May-2020, tatu: [databind#2733] may need extra handling
return _mapObjectWithDups(p, ctxt, result, key1, value1, value2, key);
}

do {
p.nextToken();
result.put(key, deserialize(p, ctxt));
final Object newValue = deserialize(p, ctxt);
final Object oldValue = result.put(key, newValue);
if (oldValue != null) {
return _mapObjectWithDups(p, ctxt, result, key, oldValue, newValue,
p.nextFieldName());
}
} while ((key = p.nextFieldName()) != null);
return result;
}

// @since 2.12 (wrt [databind#2733]
protected Object _mapObjectWithDups(JsonParser p, DeserializationContext ctxt,
final Map<String, Object> result, String key,
Object oldValue, Object newValue, String nextKey) throws IOException
{
final boolean squashDups = ctxt.isEnabled(StreamReadCapability.DUPLICATE_PROPERTIES);

if (squashDups) {
_squashDups(result, key, oldValue, newValue);
}

while (nextKey != null) {
p.nextToken();
newValue = deserialize(p, ctxt);
oldValue = result.put(nextKey, newValue);
if ((oldValue != null) && squashDups) {
_squashDups(result, key, oldValue, newValue);
}
nextKey = p.nextFieldName();
}

return result;
}

@SuppressWarnings("unchecked")
private void _squashDups(final Map<String, Object> result, String key,
Object oldValue, Object newValue)
{
if (oldValue instanceof List<?>) {
((List<Object>) oldValue).add(newValue);
result.put(key, oldValue);
} else {
ArrayList<Object> l = new ArrayList<>();
l.add(oldValue);
l.add(newValue);
result.put(key, l);
}
}

/**
* Method called to map a JSON Array into a Java Object array (Object[]).
*/
Expand Down Expand Up @@ -881,18 +931,72 @@ protected Object mapObject(JsonParser p, DeserializationContext ctxt) throws IOE
if (key == null) {
LinkedHashMap<String, Object> result = new LinkedHashMap<String, Object>(4);
result.put(key1, value1);
result.put(key2, value2);
if (result.put(key2, value2) != null) {
// 22-May-2020, tatu: [databind#2733] may need extra handling
return _mapObjectWithDups(p, ctxt, result, key1, value1, value2, key);
}
return result;
}
// And then the general case; default map size is 16
LinkedHashMap<String, Object> result = new LinkedHashMap<String, Object>();
result.put(key1, value1);
result.put(key2, value2);
if (result.put(key2, value2) != null) {
// 22-May-2020, tatu: [databind#2733] may need extra handling
return _mapObjectWithDups(p, ctxt, result, key1, value1, value2, key);
}

do {
p.nextToken();
result.put(key, deserialize(p, ctxt));
final Object newValue = deserialize(p, ctxt);
final Object oldValue = result.put(key, newValue);
if (oldValue != null) {
return _mapObjectWithDups(p, ctxt, result, key, oldValue, newValue,
p.nextFieldName());
}
} while ((key = p.nextFieldName()) != null);
return result;
}

// NOTE: copied from above (alas, no easy way to share/reuse)
// @since 2.12 (wrt [databind#2733]
protected Object _mapObjectWithDups(JsonParser p, DeserializationContext ctxt,
final Map<String, Object> result, String key,
Object oldValue, Object newValue, String nextKey) throws IOException
{
final boolean squashDups = ctxt.isEnabled(StreamReadCapability.DUPLICATE_PROPERTIES);

if (squashDups) {
_squashDups(result, key, oldValue, newValue);
}

while (nextKey != null) {
p.nextToken();
newValue = deserialize(p, ctxt);
oldValue = result.put(nextKey, newValue);
if ((oldValue != null) && squashDups) {
_squashDups(result, key, oldValue, newValue);
}
nextKey = p.nextFieldName();
}

return result;
}

// NOTE: copied from above (alas, no easy way to share/reuse)
@SuppressWarnings("unchecked")
private void _squashDups(final Map<String, Object> result, String key,
Object oldValue, Object newValue)
{
if (oldValue instanceof List<?>) {
((List<Object>) oldValue).add(newValue);
result.put(key, oldValue);
} else {
ArrayList<Object> l = new ArrayList<>();
l.add(oldValue);
l.add(newValue);
result.put(key, l);
}
}

}
}

0 comments on commit 2708892

Please sign in to comment.