diff --git a/jr-stree/src/main/java/com/fasterxml/jackson/jr/stree/JacksonJrsTreeCodec.java b/jr-stree/src/main/java/com/fasterxml/jackson/jr/stree/JacksonJrsTreeCodec.java index ed055439..6e14afe5 100644 --- a/jr-stree/src/main/java/com/fasterxml/jackson/jr/stree/JacksonJrsTreeCodec.java +++ b/jr-stree/src/main/java/com/fasterxml/jackson/jr/stree/JacksonJrsTreeCodec.java @@ -4,6 +4,7 @@ import java.util.*; import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.jr.ob.JSONObjectException; /** * {@link TreeCodec} implementation that can build "simple", immutable @@ -13,9 +14,6 @@ public class JacksonJrsTreeCodec extends TreeCodec { public static JrsMissing MISSING = JrsMissing.instance; - - public static final JacksonJrsTreeCodec SINGLETON = new JacksonJrsTreeCodec(); - protected final ObjectCodec _objectCodec; // @since 2.17 @@ -44,7 +42,7 @@ private JrsValue nodeFrom(JsonParser p) throws IOException { int tokenId = p.hasCurrentToken() ? p.currentTokenId() : p.nextToken().id(); - + switch (tokenId) { case JsonTokenId.ID_TRUE: return JrsBoolean.TRUE; @@ -55,24 +53,25 @@ private JrsValue nodeFrom(JsonParser p) throws IOException return new JrsNumber(p.getNumberValue()); case JsonTokenId.ID_STRING: return new JrsString(p.getText()); - case JsonTokenId.ID_START_ARRAY: - { - List values = _list(); - while (p.nextToken() != JsonToken.END_ARRAY) { - values.add(nodeFrom(p)); - } - return new JrsArray(values); + case JsonTokenId.ID_START_ARRAY: { + List values = _list(); + while (p.nextToken() != JsonToken.END_ARRAY) { + values.add(nodeFrom(p)); } - case JsonTokenId.ID_START_OBJECT: - { - Map values = _map(); - while (p.nextToken() != JsonToken.END_OBJECT) { - final String currentName = p.currentName(); - p.nextToken(); - values.put(currentName, nodeFrom(p)); + return new JrsArray(values); + } + case JsonTokenId.ID_START_OBJECT: { + Map values = _map(); + while (p.nextToken() != JsonToken.END_OBJECT) { + final String currentName = p.currentName(); + p.nextToken(); + JrsValue prev = values.put(currentName, nodeFrom(p)); + if (_failOnDuplicateKeys && (prev != null)) { + throw new JSONObjectException("Duplicate key (key '" + currentName + "')"); } - return new JrsObject(values); } + return new JrsObject(values); + } case JsonTokenId.ID_EMBEDDED_OBJECT: // 07-Jan-2016, tatu: won't happen with JSON, but other types like Smile // may produce binary data or such @@ -82,7 +81,7 @@ private JrsValue nodeFrom(JsonParser p) throws IOException return JrsNull.instance; default: } - throw new UnsupportedOperationException("Unsupported token id "+tokenId+" ("+p.currentToken()+")"); + throw new UnsupportedOperationException("Unsupported token id " + tokenId + " (" + p.currentToken() + ")"); } @Override @@ -134,7 +133,7 @@ public JsonParser treeAsTokens(TreeNode node) { * @since 2.8 */ public JrsBoolean booleanNode(boolean state) { - return state? JrsBoolean.TRUE : JrsBoolean.FALSE; + return state ? JrsBoolean.TRUE : JrsBoolean.FALSE; } /** diff --git a/jr-stree/src/test/java/com/fasterxml/jackson/jr/stree/DupFieldNameInTree51Test.java b/jr-stree/src/test/java/com/fasterxml/jackson/jr/stree/DupFieldNameInTree51Test.java new file mode 100644 index 00000000..0869bfdf --- /dev/null +++ b/jr-stree/src/test/java/com/fasterxml/jackson/jr/stree/DupFieldNameInTree51Test.java @@ -0,0 +1,38 @@ +package com.fasterxml.jackson.jr.stree; + +import com.fasterxml.jackson.jr.ob.JSON; +import com.fasterxml.jackson.jr.ob.JSONObjectException; + +/** + * Tests for reading content using {@link JSON} with proper + * codec registration + */ +public class DupFieldNameInTree51Test extends JacksonJrTreeTestBase +{ + private final JSON NO_DUPS_JSON = JSON.builder() + .enable(JSON.Feature.FAIL_ON_DUPLICATE_MAP_KEYS) + .register(new JrSimpleTreeExtension()) + .build(); + + private final JSON DUPS_OK_JSON = JSON.builder() + .disable(JSON.Feature.FAIL_ON_DUPLICATE_MAP_KEYS) + .register(new JrSimpleTreeExtension()) + .build(); + + // [jackson-jr#51]: test dup keys for trees too + public void testFailOnDupMapKeys() throws Exception + { + assertTrue(NO_DUPS_JSON.isEnabled(JSON.Feature.FAIL_ON_DUPLICATE_MAP_KEYS)); + final String json = "{\"a\":1,\"b\":2,\"b\":3,\"c\":4}"; + try { + /*TreeNode node =*/ NO_DUPS_JSON.treeFrom(json); + fail("Should not pass"); + } catch (JSONObjectException e) { + verifyException(e, "Duplicate key"); + } + + assertFalse(DUPS_OK_JSON.isEnabled(JSON.Feature.FAIL_ON_DUPLICATE_MAP_KEYS)); + // But should pass fine without setting + assertNotNull(DUPS_OK_JSON.treeFrom(json)); + } +} diff --git a/jr-stree/src/test/java/com/fasterxml/jackson/jr/stree/JacksonJrTreeTestBase.java b/jr-stree/src/test/java/com/fasterxml/jackson/jr/stree/JacksonJrTreeTestBase.java index 7467514b..1422c079 100644 --- a/jr-stree/src/test/java/com/fasterxml/jackson/jr/stree/JacksonJrTreeTestBase.java +++ b/jr-stree/src/test/java/com/fasterxml/jackson/jr/stree/JacksonJrTreeTestBase.java @@ -49,6 +49,6 @@ protected JSON jsonWithTreeCodec() { // 13-Feb-2020, tatu: There are 2 different ways actually.. // .treeCodec(new JacksonJrsTreeCodec()) .register(new JrSimpleTreeExtension()) - .build(); + .build(); } } diff --git a/jr-stree/src/test/java/com/fasterxml/jackson/jr/stree/failing/DupFieldNameInTree51Test.java b/jr-stree/src/test/java/com/fasterxml/jackson/jr/stree/failing/DupFieldNameInTree51Test.java deleted file mode 100644 index de6599a8..00000000 --- a/jr-stree/src/test/java/com/fasterxml/jackson/jr/stree/failing/DupFieldNameInTree51Test.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.fasterxml.jackson.jr.stree.failing; - -import com.fasterxml.jackson.jr.ob.JSON; -import com.fasterxml.jackson.jr.ob.JSONObjectException; -import com.fasterxml.jackson.jr.stree.JacksonJrTreeTestBase; - -/** - * Tests for reading content using {@link JSON} with proper - * codec registration - */ -public class DupFieldNameInTree51Test extends JacksonJrTreeTestBase -{ - private final JSON treeJSON = jsonWithTreeCodec(); - - // [jackson-jr#51]: test dup keys for trees too - public void testFailOnDupMapKeys() throws Exception - { - JSON j = JSON.builder() - .enable(JSON.Feature.FAIL_ON_DUPLICATE_MAP_KEYS) - .build(); - assertTrue(j.isEnabled(JSON.Feature.FAIL_ON_DUPLICATE_MAP_KEYS)); - final String json = "{\"a\":1,\"b\":2,\"b\":3,\"c\":4}"; - try { - /*TreeNode node =*/ treeJSON.treeFrom(json); - fail("Should not pass"); - } catch (JSONObjectException e) { - verifyException(e, "Duplicate key"); - } - } -} diff --git a/release-notes/CREDITS-2.x b/release-notes/CREDITS-2.x index 0e0c3d07..fa9294e0 100644 --- a/release-notes/CREDITS-2.x +++ b/release-notes/CREDITS-2.x @@ -52,6 +52,8 @@ Julian Honnen (@jhonnen) (2.17.0) * Contributed PoC of #25: Add support single-int Constructors (2.17.0) +* Contributed #51: Duplicate key detection does not work for (simple) Trees + (2.17.0) * Contributed fix for #93: Skip serialization of `groovy.lang.MetaClass` values to avoid `StackOverflowError` (2.17.0) diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index c2560687..1dea6504 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -15,6 +15,8 @@ Not yet released #7: Support deserialization of `int[]` (contributed by @Shounaks) +#51: Duplicate key detection does not work for (simple) Trees + (contributed by @Shounaks) #131: Add mechanism for `JacksonJrExtension`s to access state of `JSON.Feature`s 2.17.0-rc1 (26-Feb-2024)