This is an automated email from the ASF dual-hosted git repository.

rmannibucau pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/johnzon.git


The following commit(s) were added to refs/heads/master by this push:
     new 6e33ee8  [JOHNZON-339] johnzon.rejectDuplicateKeys support
6e33ee8 is described below

commit 6e33ee81dae81166a18407dd32aa5c75fd6456d5
Author: Romain Manni-Bucau <rmannibu...@gmail.com>
AuthorDate: Tue Apr 6 20:07:44 2021 +0200

    [JOHNZON-339] johnzon.rejectDuplicateKeys support
---
 .../apache/johnzon/core/AbstractJsonFactory.java   |  8 +--
 .../apache/johnzon/core/JohnzonJsonParserImpl.java |  6 +-
 .../apache/johnzon/core/JsonArrayBuilderImpl.java  | 15 +++--
 .../johnzon/core/JsonBuilderFactoryImpl.java       | 38 ++++++------
 .../apache/johnzon/core/JsonMergePatchDiff.java    |  2 +-
 .../apache/johnzon/core/JsonMergePatchImpl.java    |  2 +-
 .../apache/johnzon/core/JsonObjectBuilderImpl.java | 22 ++++---
 .../org/apache/johnzon/core/JsonProviderImpl.java  |  4 +-
 .../apache/johnzon/core/JsonReaderFactoryImpl.java | 18 +++---
 .../org/apache/johnzon/core/JsonReaderImpl.java    | 22 ++++---
 .../johnzon/core/RejectDuplicateKeysMode.java      | 71 ++++++++++++++++++++++
 .../johnzon/core/JsonObjectBuilderImplTest.java    |  9 +++
 .../apache/johnzon/core/JsonReaderImplTest.java    | 10 +++
 13 files changed, 165 insertions(+), 62 deletions(-)

diff --git 
a/johnzon-core/src/main/java/org/apache/johnzon/core/AbstractJsonFactory.java 
b/johnzon-core/src/main/java/org/apache/johnzon/core/AbstractJsonFactory.java
index ded93e3..c851717 100644
--- 
a/johnzon-core/src/main/java/org/apache/johnzon/core/AbstractJsonFactory.java
+++ 
b/johnzon-core/src/main/java/org/apache/johnzon/core/AbstractJsonFactory.java
@@ -32,11 +32,11 @@ public abstract class AbstractJsonFactory implements 
Serializable {
     
     protected final Map<String, Object> internalConfig = new HashMap<String, 
Object>();
     
-    protected AbstractJsonFactory(final Map<String, ?> config, 
Collection<String> supportedConfigKeys, Collection<String> 
defaultSupportedConfigKeys) {
-        if(config != null && config.size() > 0) {
-            
+    protected AbstractJsonFactory(final Map<String, ?> config, 
Collection<String> supportedConfigKeys,
+                                  final Collection<String> 
defaultSupportedConfigKeys) {
+        if(config != null && !config.isEmpty()) {
             if(defaultSupportedConfigKeys != null) {
-                supportedConfigKeys = new 
ArrayList<String>(supportedConfigKeys);
+                supportedConfigKeys = new ArrayList<>(supportedConfigKeys);
                 supportedConfigKeys.addAll(defaultSupportedConfigKeys);
             }
             
diff --git 
a/johnzon-core/src/main/java/org/apache/johnzon/core/JohnzonJsonParserImpl.java 
b/johnzon-core/src/main/java/org/apache/johnzon/core/JohnzonJsonParserImpl.java
index cf64a36..6edde73 100644
--- 
a/johnzon-core/src/main/java/org/apache/johnzon/core/JohnzonJsonParserImpl.java
+++ 
b/johnzon-core/src/main/java/org/apache/johnzon/core/JohnzonJsonParserImpl.java
@@ -59,7 +59,7 @@ public abstract class JohnzonJsonParserImpl implements 
JohnzonJsonParser {
             throw new IllegalStateException(current + " doesn't support 
getObject()");
         }
 
-        JsonReaderImpl jsonReader = new JsonReaderImpl(this, true, 
getCharArrayProvider());
+        JsonReaderImpl jsonReader = new JsonReaderImpl(this, true, 
getCharArrayProvider(), RejectDuplicateKeysMode.DEFAULT);
         return jsonReader.readObject();
     }
 
@@ -71,7 +71,7 @@ public abstract class JohnzonJsonParserImpl implements 
JohnzonJsonParser {
             throw new IllegalStateException(current + " doesn't support 
getArray()");
         }
 
-        JsonReaderImpl jsonReader = new JsonReaderImpl(this, true, 
getCharArrayProvider());
+        JsonReaderImpl jsonReader = new JsonReaderImpl(this, true, 
getCharArrayProvider(), RejectDuplicateKeysMode.DEFAULT);
         return jsonReader.readArray();
     }
 
@@ -81,7 +81,7 @@ public abstract class JohnzonJsonParserImpl implements 
JohnzonJsonParser {
         switch (current) {
             case START_ARRAY:
             case START_OBJECT:
-                JsonReaderImpl jsonReader = new JsonReaderImpl(this, true, 
getCharArrayProvider());
+                JsonReaderImpl jsonReader = new JsonReaderImpl(this, true, 
getCharArrayProvider(), RejectDuplicateKeysMode.DEFAULT);
                 return jsonReader.readValue();
             case VALUE_TRUE:
                 return JsonValue.TRUE;
diff --git 
a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonArrayBuilderImpl.java 
b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonArrayBuilderImpl.java
index 5ea8c65..1ee44fc 100644
--- 
a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonArrayBuilderImpl.java
+++ 
b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonArrayBuilderImpl.java
@@ -35,6 +35,7 @@ import java.util.Map;
 import org.apache.johnzon.core.util.ArrayUtil;
 
 class JsonArrayBuilderImpl implements JsonArrayBuilder, Serializable {
+    private RejectDuplicateKeysMode rejectDuplicateKeysMode;
     private List<JsonValue> tmpList;
     private BufferStrategy.BufferProvider<char[]> bufferProvider;
 
@@ -43,13 +44,17 @@ class JsonArrayBuilderImpl implements JsonArrayBuilder, 
Serializable {
     }
 
     public JsonArrayBuilderImpl(final JsonArray initialData,
-                                final BufferStrategy.BufferProvider<char[]> 
provider) {
+                                final BufferStrategy.BufferProvider<char[]> 
provider,
+                                final RejectDuplicateKeysMode 
rejectDuplicateKeysMode) {
         this.tmpList = new ArrayList<>(initialData);
         this.bufferProvider = provider;
+        this.rejectDuplicateKeysMode = rejectDuplicateKeysMode;
     }
 
-    public JsonArrayBuilderImpl(final Collection<?> initialData, final 
BufferStrategy.BufferProvider<char[]> provider) {
+    public JsonArrayBuilderImpl(final Collection<?> initialData, final 
BufferStrategy.BufferProvider<char[]> provider,
+                                final RejectDuplicateKeysMode 
rejectDuplicateKeysMode) {
         this.bufferProvider = provider;
+        this.rejectDuplicateKeysMode = rejectDuplicateKeysMode;
         this.tmpList = new ArrayList<>();
         if (!initialData.isEmpty()) {
             for (Object initialValue : initialData) {
@@ -220,12 +225,12 @@ class JsonArrayBuilderImpl implements JsonArrayBuilder, 
Serializable {
         } else if (value instanceof String) {
             add((String) value);
         } else if (value instanceof Map) {
-            add(new JsonObjectBuilderImpl(Map.class.cast(value), 
bufferProvider).build());
+            add(new JsonObjectBuilderImpl(Map.class.cast(value), 
bufferProvider, rejectDuplicateKeysMode).build());
         } else if (value instanceof Collection) {
-            add(new JsonArrayBuilderImpl(Collection.class.cast(value), 
bufferProvider).build());
+            add(new JsonArrayBuilderImpl(Collection.class.cast(value), 
bufferProvider, rejectDuplicateKeysMode).build());
         } else if (value.getClass().isArray()) {
             final Collection<Object> collection = 
ArrayUtil.newCollection(value);
-            add(new JsonArrayBuilderImpl(collection, bufferProvider).build());
+            add(new JsonArrayBuilderImpl(collection, bufferProvider, 
rejectDuplicateKeysMode).build());
         } else {
             throw new JsonException("Illegal JSON type! type=" + 
value.getClass());
         }
diff --git 
a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonBuilderFactoryImpl.java
 
b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonBuilderFactoryImpl.java
index f0cb8f5..efd10fe 100644
--- 
a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonBuilderFactoryImpl.java
+++ 
b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonBuilderFactoryImpl.java
@@ -18,8 +18,12 @@
  */
 package org.apache.johnzon.core;
 
+import javax.json.JsonArray;
+import javax.json.JsonArrayBuilder;
+import javax.json.JsonBuilderFactory;
+import javax.json.JsonObject;
+import javax.json.JsonObjectBuilder;
 import java.io.Serializable;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
@@ -27,32 +31,26 @@ import java.util.List;
 import java.util.Map;
 import java.util.logging.Logger;
 
-import javax.json.JsonArray;
-import javax.json.JsonArrayBuilder;
-import javax.json.JsonBuilderFactory;
-import javax.json.JsonObject;
-import javax.json.JsonObjectBuilder;
-
 import static java.util.Collections.emptyList;
 import static java.util.Collections.emptyMap;
 
 class JsonBuilderFactoryImpl implements JsonBuilderFactory, Serializable {
     private final Map<String, Object> internalConfig = new HashMap<String, 
Object>();
+    private RejectDuplicateKeysMode rejectDuplicateKeysMode;
     private BufferStrategy.BufferProvider<char[]> bufferProvider;
-    private static final String[] SUPPORTED_CONFIG_KEYS = new String[] {
-        // nothing yet
-    };
+    private static final List<String> SUPPORTED_CONFIG_KEYS = 
RejectDuplicateKeysMode.CONFIG_KEYS;
 
     protected JsonBuilderFactoryImpl() {
         // no-op: serialization
     }
 
-    JsonBuilderFactoryImpl(final Map<String, ?> config, final 
BufferStrategy.BufferProvider<char[]> bufferProvider) {
+    JsonBuilderFactoryImpl(final Map<String, ?> config, final 
BufferStrategy.BufferProvider<char[]> bufferProvider,
+                           final RejectDuplicateKeysMode 
rejectDuplicateKeysMode) {
         this.bufferProvider = bufferProvider;
-        if (config != null && config.size() > 0) {
-            final List<String> supportedConfigKeys = 
Arrays.asList(SUPPORTED_CONFIG_KEYS);
+        this.rejectDuplicateKeysMode = rejectDuplicateKeysMode;
+        if (config != null && !config.isEmpty()) {
             for (String configKey : config.keySet()) {
-                if(supportedConfigKeys.contains(configKey)) {
+                if(SUPPORTED_CONFIG_KEYS.contains(configKey)) {
                     internalConfig.put(configKey, config.get(configKey));
                 } else {
                     Logger.getLogger(this.getClass().getName())
@@ -64,28 +62,28 @@ class JsonBuilderFactoryImpl implements JsonBuilderFactory, 
Serializable {
 
     @Override
     public JsonObjectBuilder createObjectBuilder() {
-        return new JsonObjectBuilderImpl(emptyMap(), bufferProvider);
+        return new JsonObjectBuilderImpl(emptyMap(), bufferProvider, 
rejectDuplicateKeysMode);
     }
 
     @Override
     public JsonObjectBuilder createObjectBuilder(JsonObject initialData) {
-        return new JsonObjectBuilderImpl(initialData, bufferProvider);
+        return new JsonObjectBuilderImpl(initialData, bufferProvider, 
rejectDuplicateKeysMode);
     }
 
     @Override
     public JsonArrayBuilder createArrayBuilder() {
-        return new JsonArrayBuilderImpl(emptyList(), bufferProvider);
+        return new JsonArrayBuilderImpl(emptyList(), bufferProvider, 
rejectDuplicateKeysMode);
     }
 
 
     @Override
     public JsonArrayBuilder createArrayBuilder(JsonArray initialData) {
-        return new JsonArrayBuilderImpl(initialData, bufferProvider);
+        return new JsonArrayBuilderImpl(initialData, bufferProvider, 
rejectDuplicateKeysMode);
     }
 
     @Override
     public JsonArrayBuilder createArrayBuilder(Collection<?> initialData) {
-        return new JsonArrayBuilderImpl(initialData, bufferProvider);
+        return new JsonArrayBuilderImpl(initialData, bufferProvider, 
rejectDuplicateKeysMode);
     }
 
     @Override
@@ -95,7 +93,7 @@ class JsonBuilderFactoryImpl implements JsonBuilderFactory, 
Serializable {
 
     @Override
     public JsonObjectBuilder createObjectBuilder(Map<String, Object> 
initialValues) {
-        return new JsonObjectBuilderImpl(initialValues, bufferProvider);
+        return new JsonObjectBuilderImpl(initialValues, bufferProvider, 
rejectDuplicateKeysMode);
     }
 
 }
diff --git 
a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonMergePatchDiff.java 
b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonMergePatchDiff.java
index 1ca3bf9..3825229 100644
--- a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonMergePatchDiff.java
+++ b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonMergePatchDiff.java
@@ -45,7 +45,7 @@ class JsonMergePatchDiff extends DiffBase {
     }
 
     private JsonValue diff(JsonValue source, JsonValue target) {
-        JsonObjectBuilder builder = new JsonObjectBuilderImpl(emptyMap(), 
bufferProvider);
+        JsonObjectBuilder builder = new JsonObjectBuilderImpl(emptyMap(), 
bufferProvider, RejectDuplicateKeysMode.DEFAULT);
 
         if (isJsonObject(source) && isJsonObject(target)) {
             JsonObject srcObj = source.asJsonObject();
diff --git 
a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonMergePatchImpl.java 
b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonMergePatchImpl.java
index 56518cd..9d40f05 100644
--- a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonMergePatchImpl.java
+++ b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonMergePatchImpl.java
@@ -58,7 +58,7 @@ public class JsonMergePatchImpl implements JsonMergePatch, 
Serializable {
     }
 
     private JsonValue applyJsonObjectPatch(JsonObject jsonObject, JsonObject 
patch) {
-        JsonObjectBuilder builder = new JsonObjectBuilderImpl(jsonObject, 
bufferProvider);
+        JsonObjectBuilder builder = new JsonObjectBuilderImpl(jsonObject, 
bufferProvider, RejectDuplicateKeysMode.DEFAULT);
 
         for (Map.Entry<String, JsonValue> patchAttrib : patch.entrySet()) {
             String attribName = patchAttrib.getKey();
diff --git 
a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonObjectBuilderImpl.java 
b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonObjectBuilderImpl.java
index fafcc74..2eb4ab0 100644
--- 
a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonObjectBuilderImpl.java
+++ 
b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonObjectBuilderImpl.java
@@ -36,6 +36,7 @@ import java.util.Map;
 import org.apache.johnzon.core.util.ArrayUtil;
 
 class JsonObjectBuilderImpl implements JsonObjectBuilder, Serializable {
+    private RejectDuplicateKeysMode rejectDuplicateKeysMode;
     private BufferStrategy.BufferProvider<char[]> bufferProvider;
     private Map<String, JsonValue> attributeMap = new LinkedHashMap<>();
 
@@ -44,14 +45,18 @@ class JsonObjectBuilderImpl implements JsonObjectBuilder, 
Serializable {
     }
 
     public JsonObjectBuilderImpl(final JsonObject initialData,
-                                 final BufferStrategy.BufferProvider<char[]> 
bufferProvider) {
+                                 final BufferStrategy.BufferProvider<char[]> 
bufferProvider,
+                                 final RejectDuplicateKeysMode 
rejectDuplicateKeysMode) {
         this.bufferProvider = bufferProvider;
-        attributeMap = new LinkedHashMap<>(initialData);
+        this.rejectDuplicateKeysMode = rejectDuplicateKeysMode;
+        this.attributeMap = new LinkedHashMap<>(initialData);
     }
 
     public JsonObjectBuilderImpl(final Map<String, Object> initialValues,
-                                 final BufferStrategy.BufferProvider<char[]> 
bufferProvider) {
+                                 final BufferStrategy.BufferProvider<char[]> 
bufferProvider,
+                                 final RejectDuplicateKeysMode 
rejectDuplicateKeysMode) {
         this.bufferProvider = bufferProvider;
+        this.rejectDuplicateKeysMode = rejectDuplicateKeysMode;
         if (!initialValues.isEmpty()) {
             for (Map.Entry<String, Object> entry : initialValues.entrySet()) {
                 add(entry.getKey(), entry.getValue());
@@ -83,12 +88,12 @@ class JsonObjectBuilderImpl implements JsonObjectBuilder, 
Serializable {
         } else if (value == null) {
             addNull(name);
         } else if (value instanceof Map) {
-            add(name, new JsonObjectBuilderImpl(Map.class.cast(value), 
bufferProvider).build());
+            add(name, new JsonObjectBuilderImpl(Map.class.cast(value), 
bufferProvider, rejectDuplicateKeysMode).build());
         } else if (value instanceof Collection) {
-            add(name, new JsonArrayBuilderImpl(Collection.class.cast(value), 
bufferProvider).build());
+            add(name, new JsonArrayBuilderImpl(Collection.class.cast(value), 
bufferProvider, rejectDuplicateKeysMode).build());
         } else if (value.getClass().isArray()) {
             final Collection<Object> collection = 
ArrayUtil.newCollection(value);
-            add(name, new JsonArrayBuilderImpl(collection, 
bufferProvider).build());
+            add(name, new JsonArrayBuilderImpl(collection, bufferProvider, 
rejectDuplicateKeysMode).build());
         } else {
             throw new JsonException("Illegal JSON type! name=" + name + " 
type=" + value.getClass());
         }
@@ -172,12 +177,11 @@ class JsonObjectBuilderImpl implements JsonObjectBuilder, 
Serializable {
         return this;
     }
 
-    private void putValue(String name, JsonValue value){
+    private void putValue(final String name, final JsonValue value){
         if(name == null || value == null) {
             throw new NullPointerException("name or value/builder must not be 
null");
         }
-
-        attributeMap.put(name, value);
+        rejectDuplicateKeysMode.put().put(attributeMap, name, value);
     }
 
 
diff --git 
a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonProviderImpl.java 
b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonProviderImpl.java
index c3147d9..1bfcd71 100644
--- a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonProviderImpl.java
+++ b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonProviderImpl.java
@@ -68,7 +68,7 @@ public class JsonProviderImpl extends JsonProvider implements 
Serializable {
     private final JsonGeneratorFactory generatorFactory = new 
JsonGeneratorFactoryImpl(null);
     private final JsonWriterFactory writerFactory = new 
JsonWriterFactoryImpl(null);
     private final Supplier<JsonBuilderFactory> builderFactory = new 
Cached<>(() ->
-            new JsonBuilderFactoryImpl(null, bufferProvider.get()));
+            new JsonBuilderFactoryImpl(null, bufferProvider.get(), 
RejectDuplicateKeysMode.DEFAULT));
     private final JsonPointerFactory jsonPointerFactory;
 
     public JsonProviderImpl() {
@@ -201,7 +201,7 @@ public class JsonProviderImpl extends JsonProvider 
implements Serializable {
     public JsonBuilderFactory createBuilderFactory(final Map<String, ?> 
config) {
         final JsonBuilderFactory builderFactory = this.builderFactory.get();
         return (config == null || config.isEmpty()) ?
-                builderFactory : new JsonBuilderFactoryImpl(config, 
bufferProvider.get());
+                builderFactory : new JsonBuilderFactoryImpl(config, 
bufferProvider.get(), RejectDuplicateKeysMode.from(config));
     }
 
     @Override
diff --git 
a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonReaderFactoryImpl.java 
b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonReaderFactoryImpl.java
index 4f98812..233f8b0 100644
--- 
a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonReaderFactoryImpl.java
+++ 
b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonReaderFactoryImpl.java
@@ -18,8 +18,6 @@
  */
 package org.apache.johnzon.core;
 
-import static java.util.Arrays.asList;
-
 import java.io.InputStream;
 import java.io.Reader;
 import java.nio.charset.Charset;
@@ -32,33 +30,37 @@ import javax.json.JsonReaderFactory;
 import javax.json.stream.JsonParser;
 
 class JsonReaderFactoryImpl extends AbstractJsonFactory implements 
JsonReaderFactory {
-    static final Collection<String> SUPPORTED_CONFIG_KEYS = asList(
+    static final Collection<String> SUPPORTED_CONFIG_KEYS = 
RejectDuplicateKeysMode.CONFIG_KEYS;
 
-    );
     private final JsonParserFactoryImpl parserFactory;
+    private final RejectDuplicateKeysMode rejectDuplicateKeys;
 
     JsonReaderFactoryImpl(final Map<String, ?> config) {
         super(config, SUPPORTED_CONFIG_KEYS, 
JsonParserFactoryImpl.SUPPORTED_CONFIG_KEYS);
+        if (!internalConfig.isEmpty()) {
+            
RejectDuplicateKeysMode.CONFIG_KEYS.forEach(internalConfig::remove);
+        }
         this.parserFactory = new JsonParserFactoryImpl(internalConfig);
+        this.rejectDuplicateKeys = RejectDuplicateKeysMode.from(config);
     }
 
     @Override
     public JsonReader createReader(final Reader reader) {
-        return new JsonReaderImpl(parserFactory.createInternalParser(reader), 
parserFactory.getValueBufferProvider());
+        return new JsonReaderImpl(parserFactory.createInternalParser(reader), 
parserFactory.getValueBufferProvider(), rejectDuplicateKeys);
     }
 
     @Override
     public JsonReader createReader(final InputStream in) {
-        return new JsonReaderImpl(parserFactory.createInternalParser(in), 
parserFactory.getValueBufferProvider());
+        return new JsonReaderImpl(parserFactory.createInternalParser(in), 
parserFactory.getValueBufferProvider(), rejectDuplicateKeys);
     }
 
     @Override
     public JsonReader createReader(final InputStream in, final Charset 
charset) {
-        return new JsonReaderImpl(parserFactory.createInternalParser(in, 
charset), parserFactory.getValueBufferProvider());
+        return new JsonReaderImpl(parserFactory.createInternalParser(in, 
charset), parserFactory.getValueBufferProvider(), rejectDuplicateKeys);
     }
 
     public JsonReader createReader(final JsonParser parser) {
-        return new JsonReaderImpl(parser, 
parserFactory.getValueBufferProvider());
+        return new JsonReaderImpl(parser, 
parserFactory.getValueBufferProvider(), rejectDuplicateKeys);
     }
 
     @Override
diff --git 
a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonReaderImpl.java 
b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonReaderImpl.java
index 24de161..969ad9a 100644
--- a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonReaderImpl.java
+++ b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonReaderImpl.java
@@ -36,12 +36,14 @@ import static java.util.Collections.emptyMap;
 public class JsonReaderImpl implements JsonReader {
     private final JohnzonJsonParser parser;
     private final BufferStrategy.BufferProvider<char[]> bufferProvider;
+    private final RejectDuplicateKeysMode rejectDuplicateKeysMode;
     private boolean closed = false;
 
     private boolean subStreamReader;
 
-    public JsonReaderImpl(final JsonParser parser, final 
BufferStrategy.BufferProvider<char[]> bufferProvider) {
-        this(parser, false, bufferProvider);
+    public JsonReaderImpl(final JsonParser parser, final 
BufferStrategy.BufferProvider<char[]> bufferProvider,
+                          final RejectDuplicateKeysMode 
rejectDuplicateKeysMode) {
+        this(parser, false, bufferProvider, rejectDuplicateKeysMode);
     }
 
     /**
@@ -51,7 +53,8 @@ public class JsonReaderImpl implements JsonReader {
      * @param bufferProvider buffer provider for toString of created instances.
      */
     public JsonReaderImpl(final JsonParser parser, boolean subStreamReader,
-                          final BufferStrategy.BufferProvider<char[]> 
bufferProvider) {
+                          final BufferStrategy.BufferProvider<char[]> 
bufferProvider,
+                          final RejectDuplicateKeysMode rejectDuplicateKeys) {
         this.bufferProvider = bufferProvider;
         if (parser instanceof JohnzonJsonParser) {
             this.parser = (JohnzonJsonParser) parser;
@@ -60,6 +63,7 @@ public class JsonReaderImpl implements JsonReader {
         }
 
         this.subStreamReader = subStreamReader;
+        this.rejectDuplicateKeysMode = rejectDuplicateKeys;
     }
 
     @Override
@@ -85,14 +89,14 @@ public class JsonReaderImpl implements JsonReader {
 
         switch (next) {
             case START_OBJECT:
-                final JsonObjectBuilder objectBuilder = new 
JsonObjectBuilderImpl(emptyMap(), bufferProvider);
+                final JsonObjectBuilder objectBuilder = new 
JsonObjectBuilderImpl(emptyMap(), bufferProvider, rejectDuplicateKeysMode);
                 parseObject(objectBuilder);
                 if (!subStreamReader && parser.hasNext()) {
                     throw new JsonParsingException("Expected end of file", 
parser.getLocation());
                 }
                 return objectBuilder.build();
             case START_ARRAY:
-                final JsonArrayBuilder arrayBuilder = new 
JsonArrayBuilderImpl(emptyList(), bufferProvider);
+                final JsonArrayBuilder arrayBuilder = new 
JsonArrayBuilderImpl(emptyList(), bufferProvider, rejectDuplicateKeysMode);
                 parseArray(arrayBuilder);
                 if (!subStreamReader && parser.hasNext()) {
                     throw new JsonParsingException("Expected end of file", 
parser.getLocation());
@@ -178,13 +182,13 @@ public class JsonReaderImpl implements JsonReader {
                     break;
 
                 case START_OBJECT:
-                    JsonObjectBuilder subObject = new 
JsonObjectBuilderImpl(emptyMap(), bufferProvider);
+                    JsonObjectBuilder subObject = new 
JsonObjectBuilderImpl(emptyMap(), bufferProvider, rejectDuplicateKeysMode);
                     parseObject(subObject);
                     builder.add(key, subObject);
                     break;
 
                 case START_ARRAY:
-                    JsonArrayBuilder subArray = new 
JsonArrayBuilderImpl(emptyList(), bufferProvider);
+                    JsonArrayBuilder subArray = new 
JsonArrayBuilderImpl(emptyList(), bufferProvider, rejectDuplicateKeysMode);
                     parseArray(subArray);
                     builder.add(key, subArray);
                     break;
@@ -238,14 +242,14 @@ public class JsonReaderImpl implements JsonReader {
                     break;
 
                 case START_OBJECT:
-                    JsonObjectBuilder subObject = new 
JsonObjectBuilderImpl(emptyMap(), bufferProvider);
+                    JsonObjectBuilder subObject = new 
JsonObjectBuilderImpl(emptyMap(), bufferProvider, rejectDuplicateKeysMode);
                     parseObject(subObject);
                     builder.add(subObject);
                     break;
 
                 case START_ARRAY:
                     JsonArrayBuilder subArray = null;
-                    parseArray(subArray = new 
JsonArrayBuilderImpl(emptyList(), bufferProvider));
+                    parseArray(subArray = new 
JsonArrayBuilderImpl(emptyList(), bufferProvider, rejectDuplicateKeysMode));
                     builder.add(subArray);
                     break;
 
diff --git 
a/johnzon-core/src/main/java/org/apache/johnzon/core/RejectDuplicateKeysMode.java
 
b/johnzon-core/src/main/java/org/apache/johnzon/core/RejectDuplicateKeysMode.java
new file mode 100644
index 0000000..5e48eea
--- /dev/null
+++ 
b/johnzon-core/src/main/java/org/apache/johnzon/core/RejectDuplicateKeysMode.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.core;
+
+import javax.json.JsonException;
+import javax.json.JsonValue;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+
+import static java.util.Arrays.asList;
+
+public enum RejectDuplicateKeysMode {
+    DEFAULT(Map::put),
+    TRUE((map, k, v) -> {
+        if (map.put(k, v) != null) {
+            throw new JsonException("Rejected key: '" + k + "', already 
present");
+        }
+    });
+
+    static final List<String> CONFIG_KEYS = asList(
+            "johnzon.rejectDuplicateKeys", // our specific one
+            "org.glassfish.json.rejectDuplicateKeys" // the spec includes it 
(yes :facepalm:)
+    );
+
+    public static RejectDuplicateKeysMode from(final Map<String, ?> config) {
+        if (config == null) {
+            return DEFAULT;
+        }
+        return CONFIG_KEYS.stream()
+                .map(config::get)
+                .filter(Objects::nonNull)
+                .findFirst()
+                .map(String::valueOf)
+                .map(it -> "false".equalsIgnoreCase(it) ? "DEFAULT" : it) // 
alias to avoid to add an enum value for nothing
+                .map(it -> valueOf(it.toUpperCase(Locale.ROOT).trim()))
+                .orElse(DEFAULT);
+    }
+
+    private final Put put;
+
+    RejectDuplicateKeysMode(final Put put) {
+        this.put = put;
+    }
+
+    public Put put() {
+        return put;
+    }
+
+    @FunctionalInterface
+    public interface Put {
+        void put(final Map<String, JsonValue> receiptor, final String key, 
final JsonValue value);
+    }
+}
diff --git 
a/johnzon-core/src/test/java/org/apache/johnzon/core/JsonObjectBuilderImplTest.java
 
b/johnzon-core/src/test/java/org/apache/johnzon/core/JsonObjectBuilderImplTest.java
index 3cfd22b..be97361 100644
--- 
a/johnzon-core/src/test/java/org/apache/johnzon/core/JsonObjectBuilderImplTest.java
+++ 
b/johnzon-core/src/test/java/org/apache/johnzon/core/JsonObjectBuilderImplTest.java
@@ -28,6 +28,7 @@ import static java.util.Collections.singletonMap;
 import static org.junit.Assert.assertEquals;
 
 import javax.json.Json;
+import javax.json.JsonException;
 import javax.json.JsonObject;
 import javax.json.JsonObjectBuilder;
 import javax.json.JsonValue;
@@ -36,6 +37,14 @@ import org.junit.Assert;
 import org.junit.Test;
 
 public class JsonObjectBuilderImplTest {
+    @Test(expected = JsonException.class)
+    public void rejectedKeys() {
+        Json.createBuilderFactory(singletonMap("johnzon.rejectDuplicateKeys", 
true))
+                .createObjectBuilder()
+                .add("foo", 1)
+                .add("foo", 2);
+    }
+
     @Test
     public void createObjectBuilderMapSupport() {
         final Map<String, Object> initial = new HashMap<>();
diff --git 
a/johnzon-core/src/test/java/org/apache/johnzon/core/JsonReaderImplTest.java 
b/johnzon-core/src/test/java/org/apache/johnzon/core/JsonReaderImplTest.java
index becdf5f..5da7fa7 100644
--- a/johnzon-core/src/test/java/org/apache/johnzon/core/JsonReaderImplTest.java
+++ b/johnzon-core/src/test/java/org/apache/johnzon/core/JsonReaderImplTest.java
@@ -19,6 +19,7 @@
 package org.apache.johnzon.core;
 
 import static java.util.Collections.emptyMap;
+import static java.util.Collections.singletonMap;
 import static org.hamcrest.CoreMatchers.instanceOf;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -36,6 +37,7 @@ import java.util.Map;
 
 import javax.json.Json;
 import javax.json.JsonArray;
+import javax.json.JsonException;
 import javax.json.JsonNumber;
 import javax.json.JsonObject;
 import javax.json.JsonReader;
@@ -64,6 +66,14 @@ public class JsonReaderImplTest {
         return Collections.EMPTY_MAP;
     }
 
+    @Test(expected = JsonException.class)
+    public void rejectedKeys() {
+        Json.createReaderFactory(singletonMap("johnzon.rejectDuplicateKeys", 
true)).createReader(new StringReader("{" +
+                "\"a\":1," +
+                "\"a\":2" +
+                "}")).readObject();
+    }
+
     @Test(expected = JsonParsingException.class)
     public void badTypeObject() {
         Json.createReaderFactory(getFactoryConfig()).createReader(new 
StringReader("[]")).readObject();

Reply via email to