From 8783d39fd20ce609eccdd37c932565a6b83ba123 Mon Sep 17 00:00:00 2001 From: Rikard Pavelic Date: Fri, 21 Sep 2018 07:45:52 +0200 Subject: [PATCH] DSL-JSON v1.8.2 Improved Kotlin support (fix JsonObject detection by probing into Companion field). Improved collection support (more collections work without runtime - JsonObject and known converters). TODO: further improve collection support (known objects can be lazily initialized in the same way). --- README.md | 8 +- .../json/PrettifyOutputStream.java | 3 +- .../json/processor/ConverterTemplate.java | 10 +- .../json/processor/EnumTemplate.java | 27 ++- .../com/dslplatform/json/JsonObjectTest.java | 161 ++++++++++++++++++ .../json/generated/types/StaticJsonJava.java | 7 +- .../java/com/dslplatform/json/DslJson.java | 45 +++-- .../json/processor/AttributeInfo.java | 15 +- .../dslplatform/json/CombinedFormatTest.java | 27 ++- .../java/com/dslplatform/json/EnumTest.java | 53 ++++++ .../com/dslplatform/json/JsonObjectTest.java | 15 +- 11 files changed, 333 insertions(+), 38 deletions(-) create mode 100644 java8/src/test/java/com/dslplatform/json/JsonObjectTest.java diff --git a/README.md b/README.md index ca03e907..3e9357b1 100644 --- a/README.md +++ b/README.md @@ -187,8 +187,9 @@ To use such feature @JsonValue annotation must be placed on method or field. ### JSON pretty print -Library was designed for speed and does not support human formatted output such as alignment and newlines. -But when such output is required, there is `PrettifyStream` for converting JSON input into formatted JSON output. +Formatted output with alignments and newlines can be created via `PrettifyOutputStream`. + + dslJson.serialize(instance, new PrettifyOutputStream(outputStream)); ### External annotations @@ -367,5 +368,8 @@ When used with Gradle, configuration can be done via: ***Q***: DSL Platform annotation processor checks for new DSL compiler version on every compilation. How can I disable that? ***A***: If you specify custom `dsljson.compiler` processor option or put `dsl-compiler.exe` in project root it will use that one and will not check online for updates + ***Q***: When using Android with desugaring I get: `Exception in thread "main" java.lang.IllegalArgumentException: Type without superclass: module-info`. How can I fix that? + ***A***: It's a known Android Studio problem. It's fixed in [version 3.3](https://androidstudio.googleblog.com/2018/06/android-studio-33-canary-1-available.html) + ***Q***: What is this DSL Platform? ***A***: DSL Platform is a proprietary compiler written in C#. Since v1.7.0 DSL Platform is no longer required to create compile-time databinding. Compiler is free to use, but access to source code is licensed. If you need access to the compiler or need performance consulting [let us know](https://dsl-platform.com) diff --git a/java8/src/main/java/com/dslplatform/json/PrettifyOutputStream.java b/java8/src/main/java/com/dslplatform/json/PrettifyOutputStream.java index 693f5c0d..a9908483 100644 --- a/java8/src/main/java/com/dslplatform/json/PrettifyOutputStream.java +++ b/java8/src/main/java/com/dslplatform/json/PrettifyOutputStream.java @@ -9,6 +9,7 @@ public final class PrettifyOutputStream extends OutputStream { private static final int INDENT_CACHE_SIZE = 257; private static final boolean[] WHITESPACE = new boolean[256]; + static { WHITESPACE[9] = true; WHITESPACE[10] = true; @@ -97,7 +98,7 @@ public final void write(final byte[] bytes, final int off, final int len) throws } out.write(b); start = i + 1; - } else if(WHITESPACE[b]) { + } else if (WHITESPACE[b]) { out.write(bytes, start, i - start); start = i + 1; } else if (beginObjectOrList) { diff --git a/java8/src/main/java/com/dslplatform/json/processor/ConverterTemplate.java b/java8/src/main/java/com/dslplatform/json/processor/ConverterTemplate.java index 5c4ffd7f..0bdca679 100644 --- a/java8/src/main/java/com/dslplatform/json/processor/ConverterTemplate.java +++ b/java8/src/main/java/com/dslplatform/json/processor/ConverterTemplate.java @@ -114,7 +114,7 @@ private void asFormatConverter(final StructInfo si, final String name, final Str boolean hasConverter = context.inlinedConverters.containsKey(typeName); StructInfo target = context.structs.get(attr.typeName); if (attr.converter == null && (target == null || target.converter == null) && !hasConverter && !isStaticEnum(attr) && !attr.isJsonObject) { - List types = attr.collectionContent(context.knownTypes); + List types = attr.collectionContent(context.knownTypes, context.structs); if (target != null && attr.isEnum(context.structs)) { code.append("\t\tprivate final ").append(findConverterName(target)).append(".EnumConverter converter_").append(attr.name).append(";\n"); } else if (types != null && types.size() == 1 || (attr.isGeneric && !attr.containsStructOwnerType)) { @@ -187,7 +187,7 @@ private void asFormatConverter(final StructInfo si, final String name, final Str for (AttributeInfo attr : si.attributes.values()) { String typeName = attr.type.toString(); boolean hasConverter = context.inlinedConverters.containsKey(typeName); - List types = attr.collectionContent(context.knownTypes); + List types = attr.collectionContent(context.knownTypes, context.structs); StructInfo target = context.structs.get(attr.typeName); if (attr.converter == null && (target == null || target.converter == null) && !hasConverter && !isStaticEnum(attr) && !attr.isJsonObject) { if (target != null && attr.isEnum(context.structs)) { @@ -649,7 +649,7 @@ private void writeProperty(AttributeInfo attr, boolean checkedDefault) throws IO code.append(optimizedConverter.nonNullableEncoder("writer", readValue)).append(";\n"); } else if (target != null && attr.isEnum(context.structs)) { enumTemplate.writeName(code, target, readValue, "converter_" + attr.name); - } else if (attr.collectionContent(context.knownTypes) != null) { + } else if (attr.collectionContent(context.knownTypes, context.structs) != null) { code.append("writer.serialize(").append(readValue); if (attr.isMap) { code.append(", key_writer_").append(attr.name).append(", value_writer_").append(attr.name).append(");\n"); @@ -752,7 +752,7 @@ private void setPropertyValue(AttributeInfo attr, String alignment) throws IOExc } else { code.append("converter_").append(attr.name).append(".read(reader)"); } - } else if (attr.collectionContent(context.knownTypes) != null) { + } else if (attr.collectionContent(context.knownTypes, context.structs) != null) { context.serializeKnownCollection(attr); } else if (attr.isGeneric && !attr.containsStructOwnerType) { if (attr.isArray) { @@ -804,7 +804,7 @@ private void readPropertyValue(AttributeInfo attr, String alignment) throws IOEx } else { code.append("converter_").append(attr.name).append(".read(reader)"); } - } else if (attr.collectionContent(context.knownTypes) != null) { + } else if (attr.collectionContent(context.knownTypes, context.structs) != null) { context.serializeKnownCollection(attr); code.append(";\n"); } else if (attr.isGeneric && !attr.containsStructOwnerType) { diff --git a/java8/src/main/java/com/dslplatform/json/processor/EnumTemplate.java b/java8/src/main/java/com/dslplatform/json/processor/EnumTemplate.java index f794cb25..014a6b67 100644 --- a/java8/src/main/java/com/dslplatform/json/processor/EnumTemplate.java +++ b/java8/src/main/java/com/dslplatform/json/processor/EnumTemplate.java @@ -33,9 +33,12 @@ void writeName(Writer code, StructInfo target, String readValue, String writerNa private void writeName(Writer code, StructInfo target, String readValue, String writerName, boolean external) throws IOException { if (target.enumConstantNameSource != null) { String constantNameType = extractReturnType(target.enumConstantNameSource); + StructInfo info = context.structs.get(constantNameType); OptimizedConverter converter = context.inlinedConverters.get(constantNameType); String value = readValue + "." + target.enumConstantNameSource; - if (converter != null) { + if (info != null && info.converter != null) { + code.append(info.converter.fullName).append(".").append(info.converter.writer).append(".write(writer, ").append(value).append(");\n"); + } else if (converter != null) { code.append(converter.nonNullableEncoder("writer", value)).append(";\n"); } else { code.append(writerName).append(".write(writer, ").append(external ? readValue : value).append(");\n"); @@ -51,7 +54,9 @@ boolean isStatic(final StructInfo si) { if (si.enumConstantNameSource == null) return true; String constantNameType = extractReturnType(si.enumConstantNameSource); if (constantNameType == null) return true; - return context.inlinedConverters.get(constantNameType) != null; + StructInfo info = context.structs.get(constantNameType); + return info != null && info.converter != null + || context.inlinedConverters.get(constantNameType) != null; } void create(final StructInfo si, final String className) throws IOException { @@ -59,7 +64,8 @@ void create(final StructInfo si, final String className) throws IOException { code.append(className); code.append(">, com.dslplatform.json.JsonReader.ReadObject<").append(className).append("> {\n"); String constantNameType = extractReturnType(si.enumConstantNameSource); - OptimizedConverter converter = constantNameType != null ? context.inlinedConverters.get(constantNameType) : null; + StructInfo info = constantNameType != null ? context.structs.get(constantNameType) : null; + OptimizedConverter optimizedConverter = constantNameType != null ? context.inlinedConverters.get(constantNameType) : null; if (constantNameType != null) { code.append("\t\tprivate static final java.util.Map<").append(constantNameType).append(", ").append(className).append("> values;\n"); code.append("\t\tstatic {\n"); @@ -68,7 +74,7 @@ void create(final StructInfo si, final String className) throws IOException { code.append("\t\t\t\tvalues.put(value.").append(si.enumConstantNameSource.toString()).append(", value);\n"); code.append("\t\t\t}\n"); code.append("\t\t}\n"); - if (converter == null) { + if (optimizedConverter == null && (info == null || info.converter == null)) { code.append("\t\tprivate final com.dslplatform.json.JsonWriter.WriteObject<").append(constantNameType).append("> valueWriter;\n"); code.append("\t\tprivate final com.dslplatform.json.JsonReader.ReadObject<").append(constantNameType).append("> valueReader;\n"); code.append("\t\tpublic EnumConverter(com.dslplatform.json.DslJson __dsljson) {\n"); @@ -95,8 +101,11 @@ void create(final StructInfo si, final String className) throws IOException { code.append("\t\tpublic static ").append(className).append(" readStatic(final com.dslplatform.json.JsonReader reader) throws java.io.IOException {\n"); } if (constantNameType != null) { - if (converter != null) { - code.append("\t\t\tfinal ").append(constantNameType).append(" input = ").append(converter.nonNullableDecoder()).append("(reader);\n"); + if (info != null && info.converter != null) { + code.append("\t\t\tfinal ").append(constantNameType).append(" input = ").append(info.converter.fullName).append(".").append(info.converter.reader).append(".read(reader);\n"); + code.append("\t\t\t").append(className).append(" value = ").append("values.get(input);\n"); + } else if (optimizedConverter != null) { + code.append("\t\t\tfinal ").append(constantNameType).append(" input = ").append(optimizedConverter.nonNullableDecoder()).append("(reader);\n"); code.append("\t\t\t").append(className).append(" value = ").append("values.get(input);\n"); } else { code.append("\t\t\tfinal ").append(constantNameType).append(" input = valueReader.read(reader);\n"); @@ -107,7 +116,11 @@ void create(final StructInfo si, final String className) throws IOException { code.append("\t\t\t\tvalue = ").append(className).append(".").append(si.constants.get(0)).append(";\n"); } else { code.append("\t\t\t\tthrow new java.lang.IllegalArgumentException(\"No enum constant "); - code.append(className).append(" associated with value '\" + input + \"'\");\n"); + code.append(className).append(" associated with value '\" + input + \"'"); + if (info != null && info.converter != null) { + code.append(". When using custom objects check that custom hashCode and equals are implemented"); + } + code.append("\");\n"); } code.append("\t\t\t}\n"); code.append("\t\t\treturn value;\n"); diff --git a/java8/src/test/java/com/dslplatform/json/JsonObjectTest.java b/java8/src/test/java/com/dslplatform/json/JsonObjectTest.java new file mode 100644 index 00000000..edbad329 --- /dev/null +++ b/java8/src/test/java/com/dslplatform/json/JsonObjectTest.java @@ -0,0 +1,161 @@ +package com.dslplatform.json; + +import com.dslplatform.json.runtime.Settings; +import org.junit.Assert; +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public class JsonObjectTest { + + public static class ObjectModel { + public JsonObjectReference jsonObject; + } + + public static class ImmutableObjectModel { + public final JsonObjectReference jsonObject; + public final JsonObjectReference jsonObjectNonNull; + public ImmutableObjectModel(JsonObjectReference jsonObject, JsonObjectReference jsonObjectNonNull) { + this.jsonObject = jsonObject; + this.jsonObjectNonNull = jsonObjectNonNull; + } + } + + public static class ListModel { + public List jsonObjects; + } + + public static class CombinedModel { + public JsonObjectReference jsonObject; + public List jsonObjects; + public Map mapObjects; + } + + public static class JsonObjectReference implements JsonObject { + private final String x; + public JsonObjectReference(String x) { + this.x = x; + } + public void serialize(JsonWriter writer, boolean minimal) { + writer.writeAscii("{\"x\":"); + writer.writeString(x); + writer.writeAscii("}"); + } + + public static final JsonReader.ReadJsonObject JSON_READER = reader -> { + reader.fillName(); + reader.getNextToken(); + String x1 = reader.readString(); + reader.getNextToken(); + return new JsonObjectReference(x1); + }; + + @Override + public boolean equals(Object obj) { + return ((JsonObjectReference)obj).x.equals(this.x); + } + } + + public static class KotlinObjectModel { + public final JsonObjectReferenceKotlin jsonObject; + public KotlinObjectModel(JsonObjectReferenceKotlin jsonObject) { + this.jsonObject = jsonObject; + } + } + + public static class JsonObjectReferenceKotlin implements JsonObject { + private final String x; + public JsonObjectReferenceKotlin(String x) { + this.x = x; + } + public void serialize(JsonWriter writer, boolean minimal) { + writer.writeAscii("{\"x\":"); + writer.writeString(x); + writer.writeAscii("}"); + } + + public static final Companion Companion = new Companion(); + public static class Companion { + public JsonReader.ReadJsonObject getJSON_READER() { + return reader -> { + reader.fillName(); + reader.getNextToken(); + String x1 = reader.readString(); + reader.getNextToken(); + return new JsonObjectReferenceKotlin(x1); + }; + } + } + + @Override + public boolean equals(Object obj) { + return ((JsonObjectReferenceKotlin)obj).x.equals(this.x); + } + } + + private final DslJson dslJson = new DslJson<>(Settings.basicSetup()); + + @Test + public void simpleTest() throws IOException { + ObjectModel m = new ObjectModel(); + m.jsonObject = new JsonObjectReference("test"); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + dslJson.serialize(m, os); + Assert.assertEquals("{\"jsonObject\":{\"x\":\"test\"}}", os.toString()); + ObjectModel res = dslJson.deserialize(ObjectModel.class, os.toByteArray(), os.size()); + Assert.assertEquals(m.jsonObject, res.jsonObject); + } + + @Test + public void immutableTest() throws IOException { + ImmutableObjectModel m = new ImmutableObjectModel( + null, + new JsonObjectReference("test2")); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + dslJson.serialize(m, os); + Assert.assertEquals("{\"jsonObject\":null,\"jsonObjectNonNull\":{\"x\":\"test2\"}}", os.toString()); + ImmutableObjectModel res = dslJson.deserialize(ImmutableObjectModel.class, os.toByteArray(), os.size()); + Assert.assertNull(res.jsonObject); + Assert.assertEquals(m.jsonObjectNonNull, res.jsonObjectNonNull); + } + + @Test + public void collectionTest() throws IOException { + ListModel m = new ListModel(); + m.jsonObjects = Arrays.asList(new JsonObjectReference("test"), null, new JsonObjectReference("xxx")); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + dslJson.serialize(m, os); + Assert.assertEquals("{\"jsonObjects\":[{\"x\":\"test\"},null,{\"x\":\"xxx\"}]}", os.toString()); + ListModel res = dslJson.deserialize(ListModel.class, os.toByteArray(), os.size()); + Assert.assertEquals(m.jsonObjects, res.jsonObjects); + } + + @Test + public void complexTest() throws IOException { + CombinedModel m = new CombinedModel(); + m.jsonObject = new JsonObjectReference("test"); + m.jsonObjects = Arrays.asList(new JsonObjectReference("abc"), null, new JsonObjectReference("xxx")); + m.mapObjects = new LinkedHashMap() {{ put(1, null); put(2, new JsonObjectReference("xXx")); }}; + ByteArrayOutputStream os = new ByteArrayOutputStream(); + dslJson.serialize(m, os); + CombinedModel res = dslJson.deserialize(CombinedModel.class, os.toByteArray(), os.size()); + Assert.assertEquals(m.jsonObject, res.jsonObject); + Assert.assertEquals(m.jsonObjects, res.jsonObjects); + Assert.assertEquals(m.mapObjects, res.mapObjects); + } + + @Test + public void kotlinTest() throws IOException { + KotlinObjectModel m = new KotlinObjectModel(new JsonObjectReferenceKotlin("test")); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + dslJson.serialize(m, os); + Assert.assertEquals("{\"jsonObject\":{\"x\":\"test\"}}", os.toString()); + KotlinObjectModel res = dslJson.deserialize(KotlinObjectModel.class, os.toByteArray(), os.size()); + Assert.assertEquals(m.jsonObject, res.jsonObject); + } +} diff --git a/java8/src/test/java/com/dslplatform/json/generated/types/StaticJsonJava.java b/java8/src/test/java/com/dslplatform/json/generated/types/StaticJsonJava.java index 843d147a..89a37d7e 100644 --- a/java8/src/test/java/com/dslplatform/json/generated/types/StaticJsonJava.java +++ b/java8/src/test/java/com/dslplatform/json/generated/types/StaticJsonJava.java @@ -21,11 +21,13 @@ public static class Bytes { public static class JsonSerialization extends DslJson { private final ByteArrayOutputStream psOut = new ByteArrayOutputStream(); + private final PrettifyOutputStream prettyOut = new PrettifyOutputStream(psOut); public JsonSerialization() { super(new Settings<>().includeServiceLoader()); } private ByteArrayOutputStream stream = new ByteArrayOutputStream(); + private final PrettifyOutputStream prettyStream = new PrettifyOutputStream(psOut); public Bytes serialize(Object instance) throws IOException { stream.reset(); @@ -33,6 +35,8 @@ public Bytes serialize(Object instance) throws IOException { Bytes b = new Bytes(); b.content = stream.toByteArray(); b.length = b.content.length; + stream.reset(); + super.serialize(instance, prettyStream); return b; } @@ -42,8 +46,7 @@ public TResult deserialize( final int size) throws IOException { TResult res1 = super.deserialize(manifest, body, size); psOut.reset(); - PrettifyOutputStream prettifyStream = new PrettifyOutputStream(psOut); - prettifyStream.write(body, 0, size); + prettyOut.write(body, 0, size); super.deserialize(manifest, psOut.toByteArray(), psOut.size()); return res1; } diff --git a/library/src/main/java/com/dslplatform/json/DslJson.java b/library/src/main/java/com/dslplatform/json/DslJson.java index 7b5e03e3..d6ce54ce 100644 --- a/library/src/main/java/com/dslplatform/json/DslJson.java +++ b/library/src/main/java/com/dslplatform/json/DslJson.java @@ -1177,26 +1177,47 @@ private static void findAllSignatures(final Class manifest, final ArrayList probeForObjectReader(Class manifest, Object instance) { + Object found; + try { + found = manifest.getField("JSON_READER").get(instance); + } catch (Exception ignore) { + try { + found = manifest.getMethod("JSON_READER").invoke(instance); + } catch (Exception ignore2) { + try { + found = manifest.getMethod("getJSON_READER").invoke(instance); + } catch (Exception ignore3) { + return null; + } + } + } + return found instanceof JsonReader.ReadJsonObject + ? (JsonReader.ReadJsonObject)found + : null; + } + @SuppressWarnings("unchecked") @Nullable protected final JsonReader.ReadJsonObject getObjectReader(final Class manifest) { try { JsonReader.ReadJsonObject reader = objectReaders.get(manifest); if (reader == null) { - try { - reader = (JsonReader.ReadJsonObject) manifest.getField("JSON_READER").get(null); - } catch (Exception ignore) { + reader = probeForObjectReader(manifest, null); + if (reader == null) { + //probe in few special places try { - reader = (JsonReader.ReadJsonObject) manifest.getMethod("JSON_READER").invoke(null); - } catch (Exception ignore2) { - try { - reader = (JsonReader.ReadJsonObject) manifest.getMethod("getJSON_READER").invoke(null); - } catch (Exception ignore3) { - return null; - } + Object companion = manifest.getField("Companion").get(null); + reader = probeForObjectReader(companion.getClass(), companion); + } catch (Exception ignore) { + return null; } } - objectReaders.putIfAbsent(manifest, reader); + if (reader != null) { + objectReaders.putIfAbsent(manifest, reader); + } } return reader; } catch (final Exception ignore) { @@ -2200,7 +2221,7 @@ public void write(JsonWriter writer, @Nullable JsonObject value) { else value.serialize(writer, omitDefaults); } }; - private final JsonReader.ReadObject convertToReader(final JsonReader.ReadJsonObject decoder) { + private JsonReader.ReadObject convertToReader(final JsonReader.ReadJsonObject decoder) { return new JsonReader.ReadObject() { @Override public T read(JsonReader reader) throws IOException { diff --git a/library/src/main/java/com/dslplatform/json/processor/AttributeInfo.java b/library/src/main/java/com/dslplatform/json/processor/AttributeInfo.java index 75282837..2b02dcc8 100644 --- a/library/src/main/java/com/dslplatform/json/processor/AttributeInfo.java +++ b/library/src/main/java/com/dslplatform/json/processor/AttributeInfo.java @@ -87,21 +87,28 @@ public boolean isEnum(Map structs) { return struct != null && struct.type == ObjectType.ENUM; } + private boolean canResolveCollection(String content, Set knownTypes, Map structs) { + if (knownTypes.contains(content)) return true; + StructInfo target = structs.get(content); + //we could also say that we know how to resolve other objects, but his has to be done lazily + return target != null && target.hasKnownConversion(); + } + @Nullable - public List collectionContent(Set knownTypes) { + public List collectionContent(Set knownTypes, Map structs) { if (isArray) { String content = typeName.substring(0, typeName.length() - 2); - return knownTypes.contains(content) ? Collections.singletonList(content) : null; + return canResolveCollection(content, knownTypes, structs) ? Collections.singletonList(content) : null; } else if (isList || isSet) { int ind = typeName.indexOf('<'); String content = typeName.substring(ind + 1, typeName.length() - 1); - return knownTypes.contains(content) ? Collections.singletonList(content) : null; + return canResolveCollection(content, knownTypes, structs) ? Collections.singletonList(content) : null; } else if (isMap) { int indGen = typeName.indexOf('<'); int indComma = typeName.indexOf(',', indGen + 1); String content1 = typeName.substring(indGen + 1, indComma); String content2 = typeName.substring(indComma + 1, typeName.length() - 1); - return knownTypes.contains(content1) && knownTypes.contains(content2) + return canResolveCollection(content1, knownTypes, structs) && canResolveCollection(content2, knownTypes, structs) ? Arrays.asList(content1, content2) : null; } diff --git a/tests-java8/src/test/java/com/dslplatform/json/CombinedFormatTest.java b/tests-java8/src/test/java/com/dslplatform/json/CombinedFormatTest.java index ff62dfb8..7abec443 100644 --- a/tests-java8/src/test/java/com/dslplatform/json/CombinedFormatTest.java +++ b/tests-java8/src/test/java/com/dslplatform/json/CombinedFormatTest.java @@ -129,7 +129,6 @@ public void firstArrayThenObjectForNonEmptyCtor() throws IOException { Assert.assertArrayEquals(c.x, res2.x); } - @Test public void firstObjectThenArrayForNonEmptyCtor() throws IOException { ImmutableComposite2 c = new ImmutableComposite2( @@ -149,4 +148,30 @@ public void firstObjectThenArrayForNonEmptyCtor() throws IOException { Assert.assertEquals(c.s, res2.s); Assert.assertArrayEquals(c.x, res2.x); } + + @Test + public void withPrettyStream() throws IOException { + ImmutableComposite2 c = new ImmutableComposite2( + new int[] { 1, -1, -0 }, + Arrays.asList("abc", "def", null, "ghi"), + Double.parseDouble("123.456") + ); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + dslJson.serialize(c, new PrettifyOutputStream(os)); + Assert.assertEquals( + "{\n" + + " \"x\": [\n" + + " 1,\n" + + " -1,\n" + + " 0\n" + + " ],\n" + + " \"s\": [\n" + + " \"abc\",\n" + + " \"def\",\n" + + " null,\n" + + " \"ghi\"\n" + + " ],\n" + + " \"d\": 123.456\n" + + "}", os.toString()); + } } diff --git a/tests-java8/src/test/java/com/dslplatform/json/EnumTest.java b/tests-java8/src/test/java/com/dslplatform/json/EnumTest.java index d09a9501..ae929adc 100644 --- a/tests-java8/src/test/java/com/dslplatform/json/EnumTest.java +++ b/tests-java8/src/test/java/com/dslplatform/json/EnumTest.java @@ -340,4 +340,57 @@ public void cantChangeReferencedConverter() throws IOException { Assert.assertTrue(e.getMessage().contains("Custom reader exception")); } } + + @CompiledJson + public enum EnumWithCustomValueConverter { + ONE(BigDecimal.ONE), + PI(BigDecimal.valueOf(3.14159)), + E(BigDecimal.valueOf(2.71828)), + ZERO(BigDecimal.ZERO); + + @JsonValue + public final NumberWrapper value; + + EnumWithCustomValueConverter(BigDecimal value) { + this.value = new NumberWrapper(value); + } + } + + public static class NumberWrapper { + public final BigDecimal value; + private NumberWrapper(BigDecimal value) { + this.value = value; + } + + @Override + public int hashCode() { return value.hashCode(); } + + @Override + public boolean equals(Object obj) { + return obj instanceof NumberWrapper && ((NumberWrapper)obj).value.equals(value); + } + + @JsonConverter(target = NumberWrapper.class) + public static class NumberWrapperConverter { + public static final JsonReader.ReadObject JSON_READER = r -> { + BigDecimal value = NumberConverter.deserializeDecimal(r); + return new NumberWrapper(value); + }; + public static final JsonWriter.WriteObject JSON_WRITER = (w, v) -> { + if (v != null) NumberConverter.serialize(v.value, w); + else w.writeNull(); + }; + } + } + + @Test + public void wrapperClassWillUseSpecifiedConverter() throws IOException { + DslJson customJson = new DslJson<>(); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + List input = Arrays.asList(EnumWithCustomValueConverter.E, EnumWithCustomValueConverter.PI); + customJson.serialize(input, os); + Assert.assertEquals("[2.71828,3.14159]", os.toString("UTF-8")); + List output = customJson.deserializeList(EnumWithCustomValueConverter.class, os.toByteArray(), os.size()); + Assert.assertEquals(input, output); + } } \ No newline at end of file diff --git a/tests-java8/src/test/java/com/dslplatform/json/JsonObjectTest.java b/tests-java8/src/test/java/com/dslplatform/json/JsonObjectTest.java index 5566c5c2..2da8e890 100644 --- a/tests-java8/src/test/java/com/dslplatform/json/JsonObjectTest.java +++ b/tests-java8/src/test/java/com/dslplatform/json/JsonObjectTest.java @@ -69,8 +69,12 @@ public boolean equals(Object obj) { @CompiledJson public static class KotlinObjectModel { public JsonObjectReferenceKotlin jsonObject; - public KotlinObjectModel(JsonObjectReferenceKotlin jsonObject) { + public List jsonObjects; + public KotlinObjectModel( + JsonObjectReferenceKotlin jsonObject, + List jsonObjects) { this.jsonObject = jsonObject; + this.jsonObjects = jsonObjects; } } @@ -104,7 +108,7 @@ public boolean equals(Object obj) { } } - private final DslJson dslJson = new DslJson<>(Settings.basicSetup()); + private final DslJson dslJson = new DslJson<>(); @Test public void simpleTest() throws IOException { @@ -157,11 +161,14 @@ public void complexTest() throws IOException { @Test public void kotlinTest() throws IOException { - KotlinObjectModel m = new KotlinObjectModel(new JsonObjectReferenceKotlin("test")); + KotlinObjectModel m = new KotlinObjectModel( + new JsonObjectReferenceKotlin("test"), + Arrays.asList(new JsonObjectReferenceKotlin("abc"))); ByteArrayOutputStream os = new ByteArrayOutputStream(); dslJson.serialize(m, os); - Assert.assertEquals("{\"jsonObject\":{\"x\":\"test\"}}", os.toString()); + Assert.assertEquals("{\"jsonObject\":{\"x\":\"test\"},\"jsonObjects\":[{\"x\":\"abc\"}]}", os.toString()); KotlinObjectModel res = dslJson.deserialize(KotlinObjectModel.class, os.toByteArray(), os.size()); Assert.assertEquals(m.jsonObject, res.jsonObject); + Assert.assertEquals(m.jsonObjects, res.jsonObjects); } }