diff --git a/gson/pom.xml b/gson/pom.xml
index ad8f937de..007397956 100644
--- a/gson/pom.xml
+++ b/gson/pom.xml
@@ -117,6 +117,43 @@
2.11
test
+
+
+ com.fasterxml.jackson.dataformat
+ jackson-dataformat-yaml
+ ${jackson.version}
+ test
+
+
+ com.fasterxml.jackson.dataformat
+ jackson-dataformat-xml
+ ${jackson.version}
+ test
+
+
+ com.fasterxml.woodstox
+ woodstox-core
+ 5.1.0
+ test
+
+
+ com.fasterxml.jackson.dataformat
+ jackson-dataformat-smile
+ ${jackson.version}
+ test
+
+
+ com.fasterxml.jackson.dataformat
+ jackson-dataformat-properties
+ ${jackson.version}
+ test
+
+
+ com.fasterxml.jackson.dataformat
+ jackson-dataformat-cbor
+ ${jackson.version}
+ test
+
diff --git a/gson/src/org/immutables/gson/stream/JsonGeneratorWriter.java b/gson/src/org/immutables/gson/stream/JsonGeneratorWriter.java
index 6b726e492..988d3b249 100644
--- a/gson/src/org/immutables/gson/stream/JsonGeneratorWriter.java
+++ b/gson/src/org/immutables/gson/stream/JsonGeneratorWriter.java
@@ -17,13 +17,16 @@
import com.fasterxml.jackson.core.JsonGenerator;
import com.google.gson.stream.JsonWriter;
+
+import javax.annotation.concurrent.NotThreadSafe;
import java.io.IOException;
import java.io.Writer;
+import java.math.BigDecimal;
+import java.math.BigInteger;
import java.util.concurrent.Callable;
-import javax.annotation.concurrent.NotThreadSafe;
/**
- * {@link JsonWriter} impementation backed by Jackson's {@link JsonGenerator}.
+ * {@link JsonWriter} implementation backed by Jackson's {@link JsonGenerator}.
* Provides measurable JSON writing improvements over Gson's native implementation.
* Error reporting is might differ, however.
*/
@@ -139,16 +142,42 @@ public JsonWriter value(Number value) throws IOException {
if (value == null) {
return nullValue();
}
- double d = value.doubleValue();
- if (!isLenient()) {
- if (Double.isNaN(d) || Double.isInfinite(d)) {
- throw new IllegalArgumentException("JSON forbids NaN and infinities: " + value);
- }
+ if (value instanceof Integer) {
+ generator.writeNumber(value.intValue());
+ } else if (value instanceof Short) {
+ generator.writeNumber(value.shortValue());
+ } else if (value instanceof Long) {
+ generator.writeNumber(value.longValue());
+ } else if (value instanceof Float) {
+ float f = value.floatValue();
+ checkStrictNumber(f);
+ generator.writeNumber(f);
+ } else if (value instanceof BigInteger) {
+ generator.writeNumber((BigInteger) value);
+ } else if (value instanceof BigDecimal) {
+ BigDecimal bd = (BigDecimal) value;
+ checkStrictNumber(bd);
+ generator.writeNumber(bd);
+ } else {
+ double d = value.doubleValue();
+ checkStrictNumber(d);
+ generator.writeNumber(d);
}
- generator.writeNumber(d);
return this;
}
+ private void checkStrictNumber(Number number) {
+ if (!isLenient()) {
+ checkStrictNumber(number.doubleValue());
+ }
+ }
+
+ private void checkStrictNumber(double number) {
+ if (!isLenient() && (Double.isInfinite(number) || Double.isNaN(number))) {
+ throw new IllegalArgumentException("JSON forbids NaN and infinities: " + number);
+ }
+ }
+
@Override
public void flush() throws IOException {
generator.flush();
diff --git a/gson/src/org/immutables/gson/stream/JsonParserReader.java b/gson/src/org/immutables/gson/stream/JsonParserReader.java
index a610c4bb2..052a65394 100644
--- a/gson/src/org/immutables/gson/stream/JsonParserReader.java
+++ b/gson/src/org/immutables/gson/stream/JsonParserReader.java
@@ -16,6 +16,7 @@
package org.immutables.gson.stream;
import com.fasterxml.jackson.core.JsonLocation;
+import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonStreamContext;
import com.fasterxml.jackson.databind.util.TokenBuffer;
@@ -31,7 +32,7 @@
import static com.fasterxml.jackson.core.JsonToken.*;
/**
- * {@link JsonReader} impementation backed by Jackson's {@link JsonParser}.
+ * {@link JsonReader} implementation backed by Jackson's {@link JsonParser}.
* Provides measurable JSON parsing improvements over Gson's native implementation.
* Error reporting might differ, however.
*/
diff --git a/gson/src/org/immutables/gson/stream/XmlParserReader.java b/gson/src/org/immutables/gson/stream/XmlParserReader.java
new file mode 100644
index 000000000..0f3ba7bac
--- /dev/null
+++ b/gson/src/org/immutables/gson/stream/XmlParserReader.java
@@ -0,0 +1,39 @@
+package org.immutables.gson.stream;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.google.gson.stream.JsonReader;
+
+import javax.annotation.concurrent.NotThreadSafe;
+import java.io.IOException;
+
+/**
+ * {@link JsonReader} implementation backed by Jackson's {@link JsonParser}.
+ * This version assumes the token producer only supports strings, therefore will
+ * work with the XML and properties formats.
+ */
+@NotThreadSafe
+public class XmlParserReader extends JsonParserReader {
+ public XmlParserReader(JsonParser parser) {
+ super(parser);
+ }
+
+ @Override
+ public boolean nextBoolean() throws IOException {
+ return Boolean.parseBoolean(nextString());
+ }
+
+ @Override
+ public double nextDouble() throws IOException {
+ return Double.parseDouble(nextString());
+ }
+
+ @Override
+ public long nextLong() throws IOException {
+ return Long.parseLong(nextString());
+ }
+
+ @Override
+ public int nextInt() throws IOException {
+ return Integer.parseInt(nextString());
+ }
+}
diff --git a/gson/test/org/immutables/gson/bridge/GsonJacksonBridgeSerializationTest.java b/gson/test/org/immutables/gson/bridge/GsonJacksonBridgeSerializationTest.java
new file mode 100644
index 000000000..bbf3797e3
--- /dev/null
+++ b/gson/test/org/immutables/gson/bridge/GsonJacksonBridgeSerializationTest.java
@@ -0,0 +1,158 @@
+package org.immutables.gson.bridge;
+
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.dataformat.cbor.CBORFactory;
+import com.fasterxml.jackson.dataformat.javaprop.JavaPropsFactory;
+import com.fasterxml.jackson.dataformat.smile.SmileFactory;
+import com.fasterxml.jackson.dataformat.xml.XmlFactory;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
+import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator;
+import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import org.immutables.gson.stream.JsonGeneratorWriter;
+import org.immutables.gson.stream.JsonParserReader;
+import org.immutables.gson.stream.XmlParserReader;
+import org.junit.Assert;
+import org.junit.Test;
+
+import javax.xml.namespace.QName;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.HashMap;
+
+/**
+ * Test the various Jackson serialization formats against the Gson/Jackson bridge.
+ */
+public class GsonJacksonBridgeSerializationTest {
+
+ private final Gson gson = new GsonBuilder()
+ .registerTypeAdapterFactory(new GsonAdaptersTestObject())
+ .registerTypeAdapterFactory(new GsonAdaptersTestSubObject())
+ .create();
+
+ private TestObject createTestObject() {
+ return ImmutableTestObject.builder()
+ .intVal(123)
+ .integerVal(123)
+ .addListOfInteger(1, 2, 3, 4, 5, 6)
+ .longVal(2349823948398472394L)
+ .longObjVal(2349823948398472394L)
+ .addListOfLong(2349823948398472394L, 2349822348398472394L, 2349823948123332394L)
+ .floatVal(123123.0980F)
+ .floatObjVal(123123.0980F)
+ .addListOfFloat(123123.0980F, 124234.0980F, 1233455.0980F)
+ .doubleVal(123123123.1231231)
+ .doubleObjVal(123123123.1231231)
+ .addListOfDouble(123123.1231, 123123123.123213, 234234234.23423)
+ .bigDecimalVal(new BigDecimal(4234234.1312313123))
+ .addListOfBigDecimal(new BigDecimal(123123.123123), new BigDecimal(3534345.345345), new BigDecimal(35252.2411))
+ .booleanVal(true)
+ .booleanObjVal(false)
+ .addListOfBoolean(true, false, true)
+ .stringVal("This is a test")
+ .addListOfString("a", "b", "c")
+ .addSetOfString("a", "b", "c")
+ .putMapOfStringToString("a", "1")
+ .putMapOfStringToString("b", "2")
+ .testEnumVal(TestEnum.VALUE1)
+ .putMapOfTestEnumToString(TestEnum.VALUE2, "test2")
+ .putMapOfTestEnumToString(TestEnum.VALUE3, "test3")
+ .addListOfSubObject(ImmutableTestSubObject.builder()
+ .a("Test")
+ .b(1231)
+ .build())
+ .build();
+ }
+
+ @Test
+ public void jsonFactoryTest() throws IOException {
+ TestObject value = createTestObject();
+ JsonFactory factory = new JsonFactory();
+ Class clazz = TestObject.class;
+ ByteArrayOutputStream outputStream = testWriting(value, factory, clazz);
+ TestObject value2 = testReading(factory, clazz, outputStream);
+ Assert.assertEquals(value2.toString(), value.toString());
+ }
+
+ @Test
+ public void xmlFactoryTest() throws IOException {
+ TestObject value = createTestObject();
+ XmlFactory factory = new XmlFactory();
+ Class clazz = TestObject.class;
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ ToXmlGenerator g = factory.createGenerator(outputStream);
+ g.setNextName(QName.valueOf(clazz.getAnnotation(JacksonXmlRootElement.class).localName()));
+ JsonGeneratorWriter generatorWriter = new JsonGeneratorWriter(g);
+ gson.toJson(value, clazz, generatorWriter);
+ generatorWriter.flush();
+ TestObject value2 = testXmlReading(factory, clazz, outputStream);
+ Assert.assertEquals(value2.toString(), value.toString());
+ }
+
+ @Test
+ public void yamlFactoryTest() throws IOException {
+ TestObject value = createTestObject();
+ YAMLFactory factory = new YAMLFactory();
+ Class clazz = TestObject.class;
+ ByteArrayOutputStream outputStream = testWriting(value, factory, clazz);
+ TestObject value2 = testReading(factory, clazz, outputStream);
+ Assert.assertEquals(value2.toString(), value.toString());
+ }
+
+ @Test
+ public void smileFactoryTest() throws IOException {
+ TestObject value = createTestObject();
+ SmileFactory factory = new SmileFactory();
+ Class clazz = TestObject.class;
+ ByteArrayOutputStream outputStream = testWriting(value, factory, clazz);
+ TestObject value2 = testReading(factory, clazz, outputStream);
+ Assert.assertEquals(value2.toString(), value.toString());
+ }
+
+ @Test
+ public void propertiesFactoryTest() throws IOException {
+ TestObject value = ImmutableTestObject.copyOf(createTestObject())
+ // excluding map here because when it is de-serialized, the order is changed and the unit test fails
+ .withMapOfStringToString(new HashMap());
+ JavaPropsFactory factory = new JavaPropsFactory();
+ Class clazz = TestObject.class;
+ ByteArrayOutputStream outputStream = testWriting(value, factory, clazz);
+ TestObject value2 = testXmlReading(factory, clazz, outputStream);
+ Assert.assertEquals(value2.toString(), value.toString());
+ }
+
+ @Test
+ public void cborFactoryTest() throws IOException {
+ TestObject value = createTestObject();
+ CBORFactory factory = new CBORFactory();
+ Class clazz = TestObject.class;
+ ByteArrayOutputStream outputStream = testWriting(value, factory, clazz);
+ TestObject value2 = testReading(factory, clazz, outputStream);
+ Assert.assertEquals(value2.toString(), value.toString());
+ }
+
+ private ByteArrayOutputStream testWriting(TestObject value, JsonFactory factory, Class clazz) throws IOException {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ JsonGenerator g = factory.createGenerator(outputStream);
+ JsonGeneratorWriter generatorWriter = new JsonGeneratorWriter(g);
+ gson.toJson(value, clazz, generatorWriter);
+ generatorWriter.flush();
+ return outputStream;
+ }
+
+ private TestObject testReading(JsonFactory factory, Class clazz, ByteArrayOutputStream outputStream) throws IOException {
+ JsonParser r = factory.createParser(outputStream.toByteArray());
+ JsonParserReader parserReader = new JsonParserReader(r);
+ return gson.fromJson(parserReader, clazz);
+ }
+
+ private TestObject testXmlReading(JsonFactory factory, Class clazz, ByteArrayOutputStream outputStream) throws IOException {
+ JsonParser r = factory.createParser(outputStream.toByteArray());
+ JsonParserReader parserReader = new XmlParserReader(r);
+ return gson.fromJson(parserReader, clazz);
+ }
+}
diff --git a/gson/test/org/immutables/gson/bridge/TestEnum.java b/gson/test/org/immutables/gson/bridge/TestEnum.java
new file mode 100644
index 000000000..018e6cf55
--- /dev/null
+++ b/gson/test/org/immutables/gson/bridge/TestEnum.java
@@ -0,0 +1,5 @@
+package org.immutables.gson.bridge;
+
+public enum TestEnum {
+ VALUE1, VALUE2, VALUE3
+}
diff --git a/gson/test/org/immutables/gson/bridge/TestObject.java b/gson/test/org/immutables/gson/bridge/TestObject.java
new file mode 100644
index 000000000..a9d86b669
--- /dev/null
+++ b/gson/test/org/immutables/gson/bridge/TestObject.java
@@ -0,0 +1,65 @@
+package org.immutables.gson.bridge;
+
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
+import org.immutables.gson.Gson;
+import org.immutables.value.Value;
+
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+@Gson.TypeAdapters
+@Value.Immutable
+@JacksonXmlRootElement(localName = "test-object")
+public interface TestObject {
+ int intVal();
+
+ Integer integerVal();
+
+ List listOfInteger();
+
+ long longVal();
+
+ Long longObjVal();
+
+ List listOfLong();
+
+ float floatVal();
+
+ Float floatObjVal();
+
+ List listOfFloat();
+
+ double doubleVal();
+
+ Double doubleObjVal();
+
+ List listOfDouble();
+
+ BigDecimal bigDecimalVal();
+
+ List listOfBigDecimal();
+
+ boolean booleanVal();
+
+ Boolean booleanObjVal();
+
+ List listOfBoolean();
+
+ String stringVal();
+
+ List listOfString();
+
+ Set setOfString();
+
+ Map mapOfStringToString();
+
+ TestEnum testEnumVal();
+
+ List listOfTestEnum();
+
+ Map mapOfTestEnumToString();
+
+ List listOfSubObject();
+}
diff --git a/gson/test/org/immutables/gson/bridge/TestSubObject.java b/gson/test/org/immutables/gson/bridge/TestSubObject.java
new file mode 100644
index 000000000..20ccbc0fe
--- /dev/null
+++ b/gson/test/org/immutables/gson/bridge/TestSubObject.java
@@ -0,0 +1,12 @@
+package org.immutables.gson.bridge;
+
+import org.immutables.gson.Gson;
+import org.immutables.value.Value;
+
+@Gson.TypeAdapters
+@Value.Immutable
+public interface TestSubObject {
+ String a();
+
+ Number b();
+}