From fd48e042c0488c4a56dbdfef14e7e4fd1c3ca2e1 Mon Sep 17 00:00:00 2001 From: Jorge Bescos Gascon Date: Fri, 24 Sep 2021 09:46:03 +0200 Subject: [PATCH] provide NaN and Infinity in JsonWriter Signed-off-by: Jorge Bescos Gascon --- .../eclipse/parsson/JsonArrayBuilderImpl.java | 2 +- .../parsson/JsonObjectBuilderImpl.java | 2 +- .../org/eclipse/parsson/JsonProviderImpl.java | 21 +++- .../parsson/JsonWriterFactoryImpl.java | 6 +- .../org/eclipse/parsson/JsonWriterImpl.java | 25 ++--- .../eclipse/parsson/tests/JsonWriterTest.java | 104 +++++++++++++++++- 6 files changed, 136 insertions(+), 24 deletions(-) diff --git a/impl/src/main/java/org/eclipse/parsson/JsonArrayBuilderImpl.java b/impl/src/main/java/org/eclipse/parsson/JsonArrayBuilderImpl.java index 9bd99e4d..05eb4140 100644 --- a/impl/src/main/java/org/eclipse/parsson/JsonArrayBuilderImpl.java +++ b/impl/src/main/java/org/eclipse/parsson/JsonArrayBuilderImpl.java @@ -473,7 +473,7 @@ public int hashCode() { @Override public String toString() { StringWriter sw = new StringWriter(); - try (JsonWriter jw = new JsonWriterImpl(sw, bufferPool)) { + try (JsonWriter jw = new JsonWriterImpl(sw, bufferPool, Collections.emptyMap())) { jw.write(this); } return sw.toString(); diff --git a/impl/src/main/java/org/eclipse/parsson/JsonObjectBuilderImpl.java b/impl/src/main/java/org/eclipse/parsson/JsonObjectBuilderImpl.java index 43619d4e..2739161a 100644 --- a/impl/src/main/java/org/eclipse/parsson/JsonObjectBuilderImpl.java +++ b/impl/src/main/java/org/eclipse/parsson/JsonObjectBuilderImpl.java @@ -336,7 +336,7 @@ public int hashCode() { @Override public String toString() { StringWriter sw = new StringWriter(); - try (JsonWriter jw = new JsonWriterImpl(sw, bufferPool)) { + try (JsonWriter jw = new JsonWriterImpl(sw, bufferPool, Collections.emptyMap())) { jw.write(this); } return sw.toString(); diff --git a/impl/src/main/java/org/eclipse/parsson/JsonProviderImpl.java b/impl/src/main/java/org/eclipse/parsson/JsonProviderImpl.java index 9ae2cf7b..a2ddff05 100644 --- a/impl/src/main/java/org/eclipse/parsson/JsonProviderImpl.java +++ b/impl/src/main/java/org/eclipse/parsson/JsonProviderImpl.java @@ -87,14 +87,18 @@ public JsonGeneratorFactory createGeneratorFactory(Map config) { pool = bufferPool; } else { providerConfig = new HashMap<>(); - prettyPrinting = JsonProviderImpl.isPrettyPrintingEnabled(config); + addKnowProperty(providerConfig, config, JsonGenerator.WRITE_NAN_AS_NULLS); + addKnowProperty(providerConfig, config, JsonGenerator.WRITE_NAN_AS_STRINGS); + if (prettyPrinting=JsonProviderImpl.isPrettyPrintingEnabled(config)) { + // ?? It is set to true even if the property was false + providerConfig.put(JsonGenerator.PRETTY_PRINTING, true); + } pool = (BufferPool)config.get(BufferPool.class.getName()); if (pool != null) { providerConfig.put(BufferPool.class.getName(), pool); } else { pool = bufferPool; } - providerConfig.putAll(config); providerConfig = Collections.unmodifiableMap(providerConfig); } @@ -113,12 +117,12 @@ public JsonReader createReader(InputStream in) { @Override public JsonWriter createWriter(Writer writer) { - return new JsonWriterImpl(writer, bufferPool); + return new JsonWriterImpl(writer, bufferPool, Collections.emptyMap()); } @Override public JsonWriter createWriter(OutputStream out) { - return new JsonWriterImpl(out, bufferPool); + return new JsonWriterImpl(out, bufferPool, Collections.emptyMap()); } @Override @@ -132,7 +136,10 @@ public JsonWriterFactory createWriterFactory(Map config) { pool = bufferPool; } else { providerConfig = new HashMap<>(); + addKnowProperty(providerConfig, config, JsonGenerator.WRITE_NAN_AS_NULLS); + addKnowProperty(providerConfig, config, JsonGenerator.WRITE_NAN_AS_STRINGS); if (prettyPrinting=JsonProviderImpl.isPrettyPrintingEnabled(config)) { + // ?? It is set to true even if the property was false providerConfig.put(JsonGenerator.PRETTY_PRINTING, true); } pool = (BufferPool)config.get(BufferPool.class.getName()); @@ -279,6 +286,12 @@ public JsonBuilderFactory createBuilderFactory(Map config) { return new JsonBuilderFactoryImpl(pool, rejectDuplicateKeys); } + private void addKnowProperty(Map providerConfig, Map config, String property) { + if (config.containsKey(property)) { + providerConfig.put(property, config.get(property)); + } + } + static boolean isPrettyPrintingEnabled(Map config) { return config.containsKey(JsonGenerator.PRETTY_PRINTING); } diff --git a/impl/src/main/java/org/eclipse/parsson/JsonWriterFactoryImpl.java b/impl/src/main/java/org/eclipse/parsson/JsonWriterFactoryImpl.java index 96fd42c4..1af5e736 100644 --- a/impl/src/main/java/org/eclipse/parsson/JsonWriterFactoryImpl.java +++ b/impl/src/main/java/org/eclipse/parsson/JsonWriterFactoryImpl.java @@ -42,17 +42,17 @@ class JsonWriterFactoryImpl implements JsonWriterFactory { @Override public JsonWriter createWriter(Writer writer) { - return new JsonWriterImpl(writer, prettyPrinting, bufferPool); + return new JsonWriterImpl(writer, prettyPrinting, bufferPool, config); } @Override public JsonWriter createWriter(OutputStream out) { - return new JsonWriterImpl(out, prettyPrinting, bufferPool); + return new JsonWriterImpl(out, prettyPrinting, bufferPool, config); } @Override public JsonWriter createWriter(OutputStream out, Charset charset) { - return new JsonWriterImpl(out, charset, prettyPrinting, bufferPool); + return new JsonWriterImpl(out, charset, prettyPrinting, bufferPool, config); } @Override diff --git a/impl/src/main/java/org/eclipse/parsson/JsonWriterImpl.java b/impl/src/main/java/org/eclipse/parsson/JsonWriterImpl.java index 23c2a426..38665cef 100644 --- a/impl/src/main/java/org/eclipse/parsson/JsonWriterImpl.java +++ b/impl/src/main/java/org/eclipse/parsson/JsonWriterImpl.java @@ -22,7 +22,6 @@ import java.io.Writer; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; -import java.util.Collections; import java.util.Map; import org.eclipse.parsson.api.BufferPool; @@ -44,33 +43,33 @@ class JsonWriterImpl implements JsonWriter { private boolean writeDone; private final NoFlushOutputStream os; - JsonWriterImpl(Writer writer, BufferPool bufferPool) { - this(writer, false, bufferPool); + JsonWriterImpl(Writer writer, BufferPool bufferPool, Map config) { + this(writer, false, bufferPool, config); } - JsonWriterImpl(Writer writer, boolean prettyPrinting, BufferPool bufferPool) { + JsonWriterImpl(Writer writer, boolean prettyPrinting, BufferPool bufferPool, Map config) { generator = prettyPrinting - ? new JsonPrettyGeneratorImpl(writer, bufferPool, Collections.emptyMap()) - : new JsonGeneratorImpl(writer, bufferPool, Collections.emptyMap()); + ? new JsonPrettyGeneratorImpl(writer, bufferPool, config) + : new JsonGeneratorImpl(writer, bufferPool, config); os = null; } - JsonWriterImpl(OutputStream out, BufferPool bufferPool) { - this(out, StandardCharsets.UTF_8, false, bufferPool); + JsonWriterImpl(OutputStream out, BufferPool bufferPool, Map config) { + this(out, StandardCharsets.UTF_8, false, bufferPool, config); } - JsonWriterImpl(OutputStream out, boolean prettyPrinting, BufferPool bufferPool) { - this(out, StandardCharsets.UTF_8, prettyPrinting, bufferPool); + JsonWriterImpl(OutputStream out, boolean prettyPrinting, BufferPool bufferPool, Map config) { + this(out, StandardCharsets.UTF_8, prettyPrinting, bufferPool, config); } JsonWriterImpl(OutputStream out, Charset charset, - boolean prettyPrinting, BufferPool bufferPool) { + boolean prettyPrinting, BufferPool bufferPool, Map config) { // Decorating the given stream, so that buffered contents can be // written without actually flushing the stream. this.os = new NoFlushOutputStream(out); generator = prettyPrinting - ? new JsonPrettyGeneratorImpl(os, charset, bufferPool, Collections.emptyMap()) - : new JsonGeneratorImpl(os, charset, bufferPool, Collections.emptyMap()); + ? new JsonPrettyGeneratorImpl(os, charset, bufferPool, config) + : new JsonGeneratorImpl(os, charset, bufferPool, config); } @Override diff --git a/impl/src/test/java/org/eclipse/parsson/tests/JsonWriterTest.java b/impl/src/test/java/org/eclipse/parsson/tests/JsonWriterTest.java index a3df40d5..f049ddd5 100644 --- a/impl/src/test/java/org/eclipse/parsson/tests/JsonWriterTest.java +++ b/impl/src/test/java/org/eclipse/parsson/tests/JsonWriterTest.java @@ -16,11 +16,20 @@ package org.eclipse.parsson.tests; -import jakarta.json.*; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.StringWriter; - +import java.util.HashMap; +import java.util.Map; + +import jakarta.json.Json; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import jakarta.json.JsonValue; +import jakarta.json.JsonWriter; +import jakarta.json.JsonWriterFactory; +import jakarta.json.stream.JsonGenerator; +import jakarta.json.stream.JsonGeneratorFactory; import junit.framework.TestCase; /** @@ -214,6 +223,97 @@ public void testClose() throws Exception { assertTrue(baos.isClosed()); } + private String nameValueNanInfinity(JsonWriterFactory writerFactory) { + StringWriter writer = new StringWriter(); + JsonWriter jsonWriter = writerFactory.createWriter(writer); + JsonObject jsonObject = Json.createObjectBuilder() + .add("val1", Double.NaN) + .add("val2", 1.0) + .add("val3", 0.0) + .add("val4", Double.POSITIVE_INFINITY) + .add("val5", Double.NEGATIVE_INFINITY) + .add("val6", Json.createValue(Double.NaN)) + .add("val7", Json.createValue(1.0)) + .add("val8", Json.createValue(0.0)) + .add("val9", Json.createValue(Double.POSITIVE_INFINITY)) + .add("val10", Json.createValue(Double.NEGATIVE_INFINITY)).build(); + jsonWriter.writeObject(jsonObject); + jsonWriter.close(); + return writer.toString(); + } + + private String jsonNumberNanInfinity(JsonWriterFactory writerFactory, double value) { + StringWriter writer = new StringWriter(); + JsonWriter jsonWriter = writerFactory.createWriter(writer); + jsonWriter.write(Json.createValue(value)); + jsonWriter.close(); + return writer.toString(); + } + + public void testNanInfinityDefault() { + Map config = new HashMap<>(); + JsonWriterFactory writerFactory = Json.createWriterFactory(config); + assertEquals("{\"val1\":null,\"val2\":1.0,\"val3\":0.0,\"val4\":null,\"val5\":null,\"val6\":null,\"val7\":1.0,\"val8\":0.0,\"val9\":null,\"val10\":null}", nameValueNanInfinity(writerFactory)); + assertEquals("0.0", jsonNumberNanInfinity(writerFactory, 0.0)); + assertEquals("1.0", jsonNumberNanInfinity(writerFactory, 1.0)); + assertEquals("null", jsonNumberNanInfinity(writerFactory, Double.NaN)); + assertEquals("null", jsonNumberNanInfinity(writerFactory, Double.POSITIVE_INFINITY)); + assertEquals("null", jsonNumberNanInfinity(writerFactory, Double.NEGATIVE_INFINITY)); + } + + public void testNanInfinityWriteNanAsNull() { + Map config = new HashMap<>(); + config.put(JsonGenerator.WRITE_NAN_AS_NULLS, true); + JsonWriterFactory writerFactory = Json.createWriterFactory(config); + assertEquals("{\"val1\":null,\"val2\":1.0,\"val3\":0.0,\"val4\":null,\"val5\":null,\"val6\":null,\"val7\":1.0,\"val8\":0.0,\"val9\":null,\"val10\":null}", nameValueNanInfinity(writerFactory)); + assertEquals("0.0", jsonNumberNanInfinity(writerFactory, 0.0)); + assertEquals("1.0", jsonNumberNanInfinity(writerFactory, 1.0)); + assertEquals("null", jsonNumberNanInfinity(writerFactory, Double.NaN)); + assertEquals("null", jsonNumberNanInfinity(writerFactory, Double.POSITIVE_INFINITY)); + assertEquals("null", jsonNumberNanInfinity(writerFactory, Double.NEGATIVE_INFINITY)); + } + + public void testNanInfinityWriteNanAsString() { + Map config = new HashMap<>(); + config.put(JsonGenerator.WRITE_NAN_AS_STRINGS, true); + JsonWriterFactory writerFactory = Json.createWriterFactory(config); + assertEquals("{\"val1\":\"NaN\",\"val2\":1.0,\"val3\":0.0,\"val4\":\"+Infinity\",\"val5\":\"-Infinity\",\"val6\":\"NaN\",\"val7\":1.0,\"val8\":0.0,\"val9\":\"+Infinity\",\"val10\":\"-Infinity\"}", nameValueNanInfinity(writerFactory)); + assertEquals("0.0", jsonNumberNanInfinity(writerFactory, 0.0)); + assertEquals("1.0", jsonNumberNanInfinity(writerFactory, 1.0)); + assertEquals("\"NaN\"", jsonNumberNanInfinity(writerFactory, Double.NaN)); + assertEquals("\"+Infinity\"", jsonNumberNanInfinity(writerFactory, Double.POSITIVE_INFINITY)); + assertEquals("\"-Infinity\"", jsonNumberNanInfinity(writerFactory, Double.NEGATIVE_INFINITY)); + } + + public void testNanInfinityBothFalse() { + Map config = new HashMap<>(); + config.put(JsonGenerator.WRITE_NAN_AS_STRINGS, false); + config.put(JsonGenerator.WRITE_NAN_AS_NULLS, false); + JsonWriterFactory writerFactory = Json.createWriterFactory(config); + try { + nameValueNanInfinity(writerFactory); + fail("Expected a failure"); + } catch (NumberFormatException e) {} + try { + jsonNumberNanInfinity(writerFactory, Double.NaN); + fail("Expected a failure"); + } catch (NumberFormatException e) {} + assertEquals("0.0", jsonNumberNanInfinity(writerFactory, 0.0)); + assertEquals("1.0", jsonNumberNanInfinity(writerFactory, 1.0)); + } + + public void testNanInfinityBothTrue() { + Map config = new HashMap<>(); + config.put(JsonGenerator.WRITE_NAN_AS_STRINGS, true); + config.put(JsonGenerator.WRITE_NAN_AS_NULLS, true); + JsonWriterFactory writerFactory = Json.createWriterFactory(config); + assertEquals("{\"val1\":null,\"val2\":1.0,\"val3\":0.0,\"val4\":null,\"val5\":null,\"val6\":null,\"val7\":1.0,\"val8\":0.0,\"val9\":null,\"val10\":null}", nameValueNanInfinity(writerFactory)); + assertEquals("0.0", jsonNumberNanInfinity(writerFactory, 0.0)); + assertEquals("1.0", jsonNumberNanInfinity(writerFactory, 1.0)); + assertEquals("null", jsonNumberNanInfinity(writerFactory, Double.NaN)); + assertEquals("null", jsonNumberNanInfinity(writerFactory, Double.POSITIVE_INFINITY)); + assertEquals("null", jsonNumberNanInfinity(writerFactory, Double.NEGATIVE_INFINITY)); + } private static final class MyByteStream extends ByteArrayOutputStream { boolean closed;