Merge pull request #110 from jungm/jsonp-2.1
Implement JSON-P 2.1
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 3aaa280..b1ad933 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
@@ -46,7 +46,8 @@
public JsonArrayBuilderImpl(final JsonArray initialData,
final BufferStrategy.BufferProvider<char[]> provider,
- final RejectDuplicateKeysMode rejectDuplicateKeysMode, final JsonProviderImpl jsonProvider) {
+ final RejectDuplicateKeysMode rejectDuplicateKeysMode,
+ final JsonProviderImpl jsonProvider) {
this.tmpList = new ArrayList<>(initialData);
this.bufferProvider = provider;
this.rejectDuplicateKeysMode = rejectDuplicateKeysMode;
diff --git a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonGeneratorImpl.java b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonGeneratorImpl.java
index 53d7a43..9feb34c 100644
--- a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonGeneratorImpl.java
+++ b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonGeneratorImpl.java
@@ -449,8 +449,8 @@
try {
if (ex == null) {
flushBuffer();
+ writer.close();
}
- writer.close();
} catch (final IOException e) {
if (ex != null) {
throw ex;
diff --git a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonInMemoryParser.java b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonInMemoryParser.java
index 7dad95c..0655f70 100644
--- a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonInMemoryParser.java
+++ b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonInMemoryParser.java
@@ -174,6 +174,11 @@
}
@Override
+ public Event currentEvent() {
+ return currentEvent;
+ }
+
+ @Override
public Event current() {
if (currentEvent == null && hasNext()) {
internalNext();
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 56383f5..59b9cdd 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
@@ -199,7 +199,9 @@
if(attributeMap == null || attributeMap.isEmpty()) {
return JsonValue.EMPTY_JSON_OBJECT;
} else {
- Map<String, JsonValue> dump = (Collections.unmodifiableMap(attributeMap));
+ Map<String, JsonValue> dump = Collections.unmodifiableMap(new LinkedHashMap<>(attributeMap));
+
+ attributeMap.clear();
return new JsonObjectImpl(dump, bufferProvider);
}
}
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 31dd7e1..fce0fac 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
@@ -180,8 +180,13 @@
}
@Override
+ public JsonNumber createValue(Number number) {
+ return createValue(new BigDecimal(number.toString()));
+ }
+
+ @Override
public JsonNumber createValue(final BigInteger value) {
- return new JsonNumberImpl(new BigDecimal(value.toString()), this::checkBigDecimalScale);
+ return createValue((Number) value);
}
@Override
diff --git a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonStreamParserImpl.java b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonStreamParserImpl.java
index 8468f3e..d9b5338 100644
--- a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonStreamParserImpl.java
+++ b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonStreamParserImpl.java
@@ -370,13 +370,19 @@
}
@Override
+ public Event currentEvent() {
+ return previousEvent >= 0 && previousEvent < Event.values().length
+ ? Event.values()[previousEvent]
+ : null;
+ }
+
+ @Override
public Event current() {
if (previousEvent < 0 && hasNext()) {
internalNext();
}
- return previousEvent >= 0 && previousEvent < Event.values().length
- ? Event.values()[previousEvent]
- : null;
+
+ return currentEvent();
}
private void unreadChar() {
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
index d1fb2c5..fe89a6d 100644
--- a/johnzon-core/src/main/java/org/apache/johnzon/core/RejectDuplicateKeysMode.java
+++ b/johnzon-core/src/main/java/org/apache/johnzon/core/RejectDuplicateKeysMode.java
@@ -18,8 +18,10 @@
*/
package org.apache.johnzon.core;
+import jakarta.json.JsonConfig;
import jakarta.json.JsonException;
import jakarta.json.JsonValue;
+
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -33,9 +35,11 @@
if (map.put(k, v) != null) {
throw new JsonException("Rejected key: '" + k + "', already present");
}
- });
+ }),
+ FIRST(Map::putIfAbsent);
static final List<String> CONFIG_KEYS = asList(
+ JsonConfig.KEY_STRATEGY, // jsonp 2.1 spec
"johnzon.rejectDuplicateKeys", // our specific one
"org.glassfish.json.rejectDuplicateKeys" // the spec includes it (yes :facepalm:)
);
@@ -44,12 +48,14 @@
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 -> "false".equalsIgnoreCase(it) || "LAST".equalsIgnoreCase(it) ? "DEFAULT" : it) // aliases to avoid to add an enum value for nothing
+ .map(it -> "NONE".equalsIgnoreCase(it) ? "true" : it)
.map(it -> valueOf(it.toUpperCase(Locale.ROOT).trim()))
.orElse(DEFAULT);
}
diff --git a/johnzon-core/src/test/java/org/apache/johnzon/core/JsonInMemoryParserTest.java b/johnzon-core/src/test/java/org/apache/johnzon/core/JsonInMemoryParserTest.java
new file mode 100644
index 0000000..a219a86
--- /dev/null
+++ b/johnzon-core/src/test/java/org/apache/johnzon/core/JsonInMemoryParserTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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 org.junit.Test;
+
+import jakarta.json.stream.JsonParser;
+
+import java.util.Collections;
+
+import static org.junit.Assert.assertEquals;
+
+public class JsonInMemoryParserTest {
+ @Test
+ public void testSpecCurrentEvent() {
+ JsonParser parser = new JsonInMemoryParser(
+ new JsonObjectImpl(
+ Collections.emptyMap(),
+ BufferStrategyFactory.valueOf("QUEUE").newCharProvider(10)),
+ BufferStrategyFactory.valueOf("QUEUE").newCharProvider(10),
+ (JsonProviderImpl) JsonProviderImpl.provider());
+
+ assertEquals(null, parser.currentEvent());
+
+ parser.next();
+ assertEquals(JsonParser.Event.START_OBJECT, parser.currentEvent());
+ }
+
+
+ @Test
+ public void testJohnzonParserCurrent() {
+ JohnzonJsonParser parser = new JsonInMemoryParser(
+ new JsonObjectImpl(
+ Collections.emptyMap(),
+ BufferStrategyFactory.valueOf("QUEUE").newCharProvider(10)),
+ BufferStrategyFactory.valueOf("QUEUE").newCharProvider(10),
+ (JsonProviderImpl) JsonProviderImpl.provider());
+
+ assertEquals(JsonParser.Event.START_OBJECT, parser.current());
+ }
+}
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 c57f94f..328217c 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 org.junit.Assert.assertEquals;
import jakarta.json.Json;
+import jakarta.json.JsonConfig;
import jakarta.json.JsonException;
import jakarta.json.JsonObject;
import jakarta.json.JsonObjectBuilder;
@@ -45,6 +46,36 @@
.add("foo", 2);
}
+ @Test(expected = JsonException.class)
+ public void keyStrategyNone() {
+ Json.createBuilderFactory(singletonMap(JsonConfig.KEY_STRATEGY, JsonConfig.KeyStrategy.NONE))
+ .createObjectBuilder()
+ .add("foo", 1)
+ .add("foo", 2);
+ }
+
+ @Test
+ public void keyStrategyFirst() {
+ JsonObject built = Json.createBuilderFactory(singletonMap(JsonConfig.KEY_STRATEGY, JsonConfig.KeyStrategy.FIRST))
+ .createObjectBuilder()
+ .add("foo", 1)
+ .add("foo", 2)
+ .build();
+
+ assertEquals("{\"foo\":1}", built.toString());
+ }
+
+ @Test
+ public void keyStrategyLast() {
+ JsonObject built = Json.createBuilderFactory(singletonMap(JsonConfig.KEY_STRATEGY, JsonConfig.KeyStrategy.LAST))
+ .createObjectBuilder()
+ .add("foo", 1)
+ .add("foo", 2)
+ .build();
+
+ assertEquals("{\"foo\":2}", built.toString());
+ }
+
@Test
public void createObjectBuilderMapSupport() {
final Map<String, Object> initial = new HashMap<>();
diff --git a/johnzon-core/src/test/java/org/apache/johnzon/core/JsonProviderTest.java b/johnzon-core/src/test/java/org/apache/johnzon/core/JsonProviderTest.java
index 412df42..ea78422 100644
--- a/johnzon-core/src/test/java/org/apache/johnzon/core/JsonProviderTest.java
+++ b/johnzon-core/src/test/java/org/apache/johnzon/core/JsonProviderTest.java
@@ -83,4 +83,14 @@
Assert.assertEquals(bi, val.bigIntegerValue());
}
+
+ @Test
+ public void testJsonCreateValueNumber() {
+ Number someNumber = 42;
+ JsonNumber val = Json.createValue(someNumber);
+
+ Assert.assertNotNull(val);
+ Assert.assertEquals(JsonValue.ValueType.NUMBER, val.getValueType());
+ Assert.assertEquals(someNumber, val.intValue());
+ }
}
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 66fe7fc..dc5f243 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
@@ -37,6 +37,7 @@
import jakarta.json.Json;
import jakarta.json.JsonArray;
+import jakarta.json.JsonConfig;
import jakarta.json.JsonException;
import jakarta.json.JsonNumber;
import jakarta.json.JsonObject;
@@ -74,6 +75,35 @@
"}")).readObject();
}
+ @Test(expected = JsonException.class)
+ public void keyStrategyNone() {
+ Json.createReaderFactory(singletonMap(JsonConfig.KEY_STRATEGY, JsonConfig.KeyStrategy.NONE)).createReader(new StringReader("{" +
+ "\"a\":1," +
+ "\"a\":2" +
+ "}")).readObject();
+ }
+
+
+ @Test
+ public void keyStrategyFirstKey() {
+ JsonObject object = Json.createReaderFactory(singletonMap(JsonConfig.KEY_STRATEGY, JsonConfig.KeyStrategy.FIRST)).createReader(new StringReader("{" +
+ "\"a\":1," +
+ "\"a\":2" +
+ "}")).readObject();
+
+ assertEquals(1, object.getInt("a"));
+ }
+
+ @Test
+ public void keyStrategyLastKey() {
+ JsonObject object = Json.createReaderFactory(singletonMap(JsonConfig.KEY_STRATEGY, JsonConfig.KeyStrategy.LAST)).createReader(new StringReader("{" +
+ "\"a\":1," +
+ "\"a\":2" +
+ "}")).readObject();
+
+ assertEquals(2, object.getInt("a"));
+ }
+
@Test(expected = JsonParsingException.class)
public void badTypeObject() {
Json.createReaderFactory(getFactoryConfig()).createReader(new StringReader("[]")).readObject();
diff --git a/johnzon-core/src/test/java/org/apache/johnzon/core/JsonStreamParserImplTest.java b/johnzon-core/src/test/java/org/apache/johnzon/core/JsonStreamParserImplTest.java
index 2440650..bf767ab 100644
--- a/johnzon-core/src/test/java/org/apache/johnzon/core/JsonStreamParserImplTest.java
+++ b/johnzon-core/src/test/java/org/apache/johnzon/core/JsonStreamParserImplTest.java
@@ -33,6 +33,37 @@
public class JsonStreamParserImplTest {
@Test
+ public void testSpecCurrentEvent() {
+ String json = "{}";
+
+ final JsonParser parser = new JsonStreamParserImpl(new ByteArrayInputStream(json
+ .getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8,
+ 10,
+ BufferStrategyFactory.valueOf("QUEUE").newCharProvider(10),
+ BufferStrategyFactory.valueOf("QUEUE").newCharProvider(10),
+ true, (JsonProviderImpl) JsonProviderImpl.provider());
+
+ assertEquals(null, parser.currentEvent());
+
+ parser.next();
+ assertEquals(JsonParser.Event.START_OBJECT, parser.currentEvent());
+ }
+
+ @Test
+ public void testJohnzonParserCurrent() {
+ String json = "{}";
+
+ final JohnzonJsonParser parser = new JsonStreamParserImpl(new ByteArrayInputStream(json
+ .getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8,
+ 10,
+ BufferStrategyFactory.valueOf("QUEUE").newCharProvider(10),
+ BufferStrategyFactory.valueOf("QUEUE").newCharProvider(10),
+ true, (JsonProviderImpl) JsonProviderImpl.provider());
+
+ assertEquals(JsonParser.Event.START_OBJECT, parser.current());
+ }
+
+ @Test
public void ensureNoArrayBoundErrorWhenOverflow() throws IOException {
final String json = new JsonObjectBuilderImpl(
emptyMap(),