From 14762e177cd72cc81d070a17aa9ee9cc634550de Mon Sep 17 00:00:00 2001 From: Fengyun Date: Sat, 2 Feb 2019 16:04:39 -0800 Subject: [PATCH 1/4] Support simpleValue type for @JsonUnwrapper, the feature is similar as @JsonValue in jackson --- pom.xml | 5 +- .../jsoniter/output/CodegenImplObject.java | 74 +++++++++++-------- .../output/ReflectionObjectEncoder.java | 60 ++++++++------- .../com/jsoniter/spi/UnwrapperDescriptor.java | 9 ++- .../output/TestAnnotationJsonUnwrapper.java | 19 +++++ 5 files changed, 107 insertions(+), 60 deletions(-) diff --git a/pom.xml b/pom.xml index b3f817c0..ef93e161 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 com.jsoniter - 0.9.23 + 0.9.24-SNAPSHOT jsoniter json iterator jsoniter (json-iterator) is fast and flexible JSON parser available in Java and Go @@ -155,6 +155,8 @@ + + org.sonatype.plugins nexus-staging-maven-plugin diff --git a/src/main/java/com/jsoniter/output/CodegenImplObject.java b/src/main/java/com/jsoniter/output/CodegenImplObject.java index 2e07f651..1e666adb 100644 --- a/src/main/java/com/jsoniter/output/CodegenImplObject.java +++ b/src/main/java/com/jsoniter/output/CodegenImplObject.java @@ -12,41 +12,51 @@ public static CodegenResult genObject(ClassInfo classInfo) { List encodeTos = desc.encodeTos(); ctx.append(String.format("public static void encode_(%s obj, com.jsoniter.output.JsonStream stream) throws java.io.IOException {", classInfo.clazz.getCanonicalName())); if (hasFieldOutput(desc)) { - int notFirst = 0; - if (noIndention) { - ctx.buffer('{'); - } else { - ctx.append("stream.writeObjectStart();"); - } - for (EncodeTo encodeTo : encodeTos) { - notFirst = genField(ctx, encodeTo.binding, encodeTo.toName, notFirst); + boolean isSimpleValue = encodeTos.isEmpty() && desc.unwrappers.size() == 1 && desc.unwrappers.get(0).isSimpleValue; + if (isSimpleValue) { + UnwrapperDescriptor unwrapper = desc.unwrappers.get(0); + ctx.append(String.format("Object simpleValue = obj.%s();", unwrapper.method.getName())); + ctx.append("if (simpleValue == null) { stream.writeNull(); } else {"); + CodegenImplNative.genWriteOp(ctx, "simpleValue", unwrapper.mapValueTypeLiteral.getType(), true); + ctx.append("}"); } - for (UnwrapperDescriptor unwrapper : desc.unwrappers) { - if (unwrapper.isMap) { - ctx.append(String.format("java.util.Map map = (java.util.Map)obj.%s();", unwrapper.method.getName())); - ctx.append("java.util.Iterator iter = map.entrySet().iterator();"); - ctx.append("while(iter.hasNext()) {"); - ctx.append("java.util.Map.Entry entry = (java.util.Map.Entry)iter.next();"); - notFirst = appendComma(ctx, notFirst); - ctx.append("stream.writeObjectField(entry.getKey().toString());"); - ctx.append("if (entry.getValue() == null) { stream.writeNull(); } else {"); - CodegenImplNative.genWriteOp(ctx, "entry.getValue()", unwrapper.mapValueTypeLiteral.getType(), true); - ctx.append("}"); - ctx.append("}"); + else { + int notFirst = 0; + if (noIndention) { + ctx.buffer('{'); } else { - notFirst = appendComma(ctx, notFirst); - ctx.append(String.format("obj.%s(stream);", unwrapper.method.getName())); + ctx.append("stream.writeObjectStart();"); } - } - if (noIndention) { - ctx.buffer('}'); - } else { - if (notFirst == 1) { // definitely not first - ctx.append("stream.writeObjectEnd();"); - } else if (notFirst == 2) { // // maybe not first, previous field is omitNull - ctx.append("if (notFirst) { stream.writeObjectEnd(); } else { stream.write('}'); }"); - } else { // this is the first - ctx.append("stream.write('}');"); + for (EncodeTo encodeTo : encodeTos) { + notFirst = genField(ctx, encodeTo.binding, encodeTo.toName, notFirst); + } + for (UnwrapperDescriptor unwrapper : desc.unwrappers) { + if (unwrapper.isMap) { + ctx.append(String.format("java.util.Map map = (java.util.Map)obj.%s();", unwrapper.method.getName())); + ctx.append("java.util.Iterator iter = map.entrySet().iterator();"); + ctx.append("while(iter.hasNext()) {"); + ctx.append("java.util.Map.Entry entry = (java.util.Map.Entry)iter.next();"); + notFirst = appendComma(ctx, notFirst); + ctx.append("stream.writeObjectField(entry.getKey().toString());"); + ctx.append("if (entry.getValue() == null) { stream.writeNull(); } else {"); + CodegenImplNative.genWriteOp(ctx, "entry.getValue()", unwrapper.mapValueTypeLiteral.getType(), true); + ctx.append("}"); + ctx.append("}"); + } else { + notFirst = appendComma(ctx, notFirst); + ctx.append(String.format("obj.%s(stream);", unwrapper.method.getName())); + } + } + if (noIndention) { + ctx.buffer('}'); + } else { + if (notFirst == 1) { // definitely not first + ctx.append("stream.writeObjectEnd();"); + } else if (notFirst == 2) { // // maybe not first, previous field is omitNull + ctx.append("if (notFirst) { stream.writeObjectEnd(); } else { stream.write('}'); }"); + } else { // this is the first + ctx.append("stream.write('}');"); + } } } } else { diff --git a/src/main/java/com/jsoniter/output/ReflectionObjectEncoder.java b/src/main/java/com/jsoniter/output/ReflectionObjectEncoder.java index 99c256c3..39a60466 100644 --- a/src/main/java/com/jsoniter/output/ReflectionObjectEncoder.java +++ b/src/main/java/com/jsoniter/output/ReflectionObjectEncoder.java @@ -67,42 +67,50 @@ private void enocde_(Object obj, JsonStream stream) throws Exception { stream.writeNull(); return; } - stream.writeObjectStart(); - boolean notFirst = false; - for (EncodeTo encodeTo : fields) { - Object val = encodeTo.binding.field.get(obj); - notFirst = writeEncodeTo(stream, notFirst, encodeTo, val); + boolean isSimpleValue = fields.isEmpty() && getters.isEmpty() && desc.unwrappers.size() == 1 && desc.unwrappers.get(0).isSimpleValue; + if (isSimpleValue) { + UnwrapperDescriptor unwrapper = desc.unwrappers.get(0); + Object simpleValue = unwrapper.method.invoke(obj); + stream.writeVal(unwrapper.mapValueTypeLiteral, simpleValue); } - for (EncodeTo encodeTo : getters) { - Object val = encodeTo.binding.method.invoke(obj); - notFirst = writeEncodeTo(stream, notFirst, encodeTo, val); - } - for (UnwrapperDescriptor unwrapper : desc.unwrappers) { - if (unwrapper.isMap) { - Map map = (Map) unwrapper.method.invoke(obj); - for (Map.Entry entry : map.entrySet()) { + else { + stream.writeObjectStart(); + boolean notFirst = false; + for (EncodeTo encodeTo : fields) { + Object val = encodeTo.binding.field.get(obj); + notFirst = writeEncodeTo(stream, notFirst, encodeTo, val); + } + for (EncodeTo encodeTo : getters) { + Object val = encodeTo.binding.method.invoke(obj); + notFirst = writeEncodeTo(stream, notFirst, encodeTo, val); + } + for (UnwrapperDescriptor unwrapper : desc.unwrappers) { + if (unwrapper.isMap) { + Map map = (Map) unwrapper.method.invoke(obj); + for (Map.Entry entry : map.entrySet()) { + if (notFirst) { + stream.writeMore(); + } else { + notFirst = true; + } + stream.writeObjectField(entry.getKey().toString()); + stream.writeVal(unwrapper.mapValueTypeLiteral, entry.getValue()); + } + } else { if (notFirst) { stream.writeMore(); } else { notFirst = true; } - stream.writeObjectField(entry.getKey().toString()); - stream.writeVal(unwrapper.mapValueTypeLiteral, entry.getValue()); + unwrapper.method.invoke(obj, stream); } + } + if (notFirst) { + stream.writeObjectEnd(); } else { - if (notFirst) { - stream.writeMore(); - } else { - notFirst = true; - } - unwrapper.method.invoke(obj, stream); + stream.write('}'); } } - if (notFirst) { - stream.writeObjectEnd(); - } else { - stream.write('}'); - } } private boolean writeEncodeTo(JsonStream stream, boolean notFirst, EncodeTo encodeTo, Object val) throws IOException { diff --git a/src/main/java/com/jsoniter/spi/UnwrapperDescriptor.java b/src/main/java/com/jsoniter/spi/UnwrapperDescriptor.java index eb758054..37a97a09 100644 --- a/src/main/java/com/jsoniter/spi/UnwrapperDescriptor.java +++ b/src/main/java/com/jsoniter/spi/UnwrapperDescriptor.java @@ -13,6 +13,8 @@ public class UnwrapperDescriptor { public boolean isMap; + public boolean isSimpleValue; + public TypeLiteral mapValueTypeLiteral; public UnwrapperDescriptor(Method method) { @@ -30,7 +32,12 @@ public UnwrapperDescriptor(Method method) { } } else if (isStreamUnwrapper(method)) { this.isMap = false; - } else { + } else if (method.getParameterTypes().length == 0 && method.getReturnType() != void.class) { //A getter + this.isSimpleValue = true; + Type mapType = method.getGenericReturnType(); + mapValueTypeLiteral = TypeLiteral.create(mapType); + } + else { throw new JsonException("invalid unwrapper method signature: " + method); } } diff --git a/src/test/java/com/jsoniter/output/TestAnnotationJsonUnwrapper.java b/src/test/java/com/jsoniter/output/TestAnnotationJsonUnwrapper.java index 9ad9f66c..51cac80f 100644 --- a/src/test/java/com/jsoniter/output/TestAnnotationJsonUnwrapper.java +++ b/src/test/java/com/jsoniter/output/TestAnnotationJsonUnwrapper.java @@ -48,4 +48,23 @@ public void test_unwrapper_with_map() throws IOException { stream.close(); assertEquals("{\"100\":\"hello\"}", baos.toString()); } + + + /** + * Consider the getter result as the entire json object + * Similar as @JsonValue in jackson + */ + public static class TestObject3 { + @JsonUnwrapper + public String getValue() { + return "hello"; + } + } + + public void test_unwrapper_with_simple_value() throws IOException { + TestObject3 obj = new TestObject3(); + stream.writeVal(obj); + stream.close(); + assertEquals("\"hello\"", baos.toString()); + } } From a019117fe396726a2a437d8b33f0829d89791324 Mon Sep 17 00:00:00 2001 From: Fengyun Date: Sat, 2 Feb 2019 18:02:18 -0800 Subject: [PATCH 2/4] Verify @JsonUnwrapper's syntax, make sure there is only one @JsonUnwrapper has simple value. --- .../java/com/jsoniter/output/CodegenImplObject.java | 8 +++++++- .../jsoniter/output/ReflectionObjectEncoder.java | 8 +++++++- src/main/java/com/jsoniter/spi/Config.java | 13 +++++++++++++ .../java/com/jsoniter/spi/UnwrapperDescriptor.java | 2 +- .../output/TestAnnotationJsonUnwrapper.java | 11 +++++++++++ 5 files changed, 39 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/jsoniter/output/CodegenImplObject.java b/src/main/java/com/jsoniter/output/CodegenImplObject.java index 1e666adb..b2f03c88 100644 --- a/src/main/java/com/jsoniter/output/CodegenImplObject.java +++ b/src/main/java/com/jsoniter/output/CodegenImplObject.java @@ -12,7 +12,13 @@ public static CodegenResult genObject(ClassInfo classInfo) { List encodeTos = desc.encodeTos(); ctx.append(String.format("public static void encode_(%s obj, com.jsoniter.output.JsonStream stream) throws java.io.IOException {", classInfo.clazz.getCanonicalName())); if (hasFieldOutput(desc)) { - boolean isSimpleValue = encodeTos.isEmpty() && desc.unwrappers.size() == 1 && desc.unwrappers.get(0).isSimpleValue; + boolean isSimpleValue = false; + for(UnwrapperDescriptor unwarpper: desc.unwrappers) { + if (unwarpper.isSimpleValue) { + isSimpleValue = true; + break; + } + } if (isSimpleValue) { UnwrapperDescriptor unwrapper = desc.unwrappers.get(0); ctx.append(String.format("Object simpleValue = obj.%s();", unwrapper.method.getName())); diff --git a/src/main/java/com/jsoniter/output/ReflectionObjectEncoder.java b/src/main/java/com/jsoniter/output/ReflectionObjectEncoder.java index 39a60466..ec5fea82 100644 --- a/src/main/java/com/jsoniter/output/ReflectionObjectEncoder.java +++ b/src/main/java/com/jsoniter/output/ReflectionObjectEncoder.java @@ -67,7 +67,13 @@ private void enocde_(Object obj, JsonStream stream) throws Exception { stream.writeNull(); return; } - boolean isSimpleValue = fields.isEmpty() && getters.isEmpty() && desc.unwrappers.size() == 1 && desc.unwrappers.get(0).isSimpleValue; + boolean isSimpleValue = false; + for(UnwrapperDescriptor unwarpper: desc.unwrappers) { + if (unwarpper.isSimpleValue) { + isSimpleValue = true; + break; + } + } if (isSimpleValue) { UnwrapperDescriptor unwrapper = desc.unwrappers.get(0); Object simpleValue = unwrapper.method.invoke(obj); diff --git a/src/main/java/com/jsoniter/spi/Config.java b/src/main/java/com/jsoniter/spi/Config.java index 5b0d6c98..4183933e 100644 --- a/src/main/java/com/jsoniter/spi/Config.java +++ b/src/main/java/com/jsoniter/spi/Config.java @@ -264,6 +264,19 @@ private void detectUnwrappers(ClassDescriptor desc, List allMethods) { } desc.unwrappers.add(new UnwrapperDescriptor(method)); } + if (!desc.unwrappers.isEmpty()) { + boolean isSimpleValue = false; + for(UnwrapperDescriptor unwarpper: desc.unwrappers) { + if (unwarpper.isSimpleValue) { + if (isSimpleValue) { + throw new JsonException("Multiple @JsonUnwrapper on simple value getter"); + } + else { + isSimpleValue = true; + } + } + } + } } private void detectWrappers(ClassDescriptor desc, List allMethods) { diff --git a/src/main/java/com/jsoniter/spi/UnwrapperDescriptor.java b/src/main/java/com/jsoniter/spi/UnwrapperDescriptor.java index 37a97a09..d18a5976 100644 --- a/src/main/java/com/jsoniter/spi/UnwrapperDescriptor.java +++ b/src/main/java/com/jsoniter/spi/UnwrapperDescriptor.java @@ -34,7 +34,7 @@ public UnwrapperDescriptor(Method method) { this.isMap = false; } else if (method.getParameterTypes().length == 0 && method.getReturnType() != void.class) { //A getter this.isSimpleValue = true; - Type mapType = method.getGenericReturnType(); + Type mapType = method.getReturnType(); mapValueTypeLiteral = TypeLiteral.create(mapType); } else { diff --git a/src/test/java/com/jsoniter/output/TestAnnotationJsonUnwrapper.java b/src/test/java/com/jsoniter/output/TestAnnotationJsonUnwrapper.java index 51cac80f..7ba8f228 100644 --- a/src/test/java/com/jsoniter/output/TestAnnotationJsonUnwrapper.java +++ b/src/test/java/com/jsoniter/output/TestAnnotationJsonUnwrapper.java @@ -55,6 +55,17 @@ public void test_unwrapper_with_map() throws IOException { * Similar as @JsonValue in jackson */ public static class TestObject3 { + + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + @JsonUnwrapper public String getValue() { return "hello"; From 1a04233f8938b72a32ab8bc0ee7c6f35fd894cac Mon Sep 17 00:00:00 2001 From: Fengyun Date: Sat, 2 Feb 2019 18:05:31 -0800 Subject: [PATCH 3/4] Revert the changes in pom.xml --- pom.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ef93e161..29fab04d 100644 --- a/pom.xml +++ b/pom.xml @@ -156,7 +156,6 @@ - + org.sonatype.plugins nexus-staging-maven-plugin From 7441d951b9d32a260681e20cfef71fea9722822b Mon Sep 17 00:00:00 2001 From: Fengyun Date: Fri, 8 Feb 2019 09:01:45 -0800 Subject: [PATCH 4/4] Simplify the code to avoid duplicate loop --- src/main/java/com/jsoniter/spi/Config.java | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/jsoniter/spi/Config.java b/src/main/java/com/jsoniter/spi/Config.java index 4183933e..e689d469 100644 --- a/src/main/java/com/jsoniter/spi/Config.java +++ b/src/main/java/com/jsoniter/spi/Config.java @@ -255,6 +255,7 @@ public void updateClassDescriptor(ClassDescriptor desc) { } private void detectUnwrappers(ClassDescriptor desc, List allMethods) { + boolean isSimpleValue = false; for (Method method : allMethods) { if (Modifier.isStatic(method.getModifiers())) { continue; @@ -262,20 +263,16 @@ private void detectUnwrappers(ClassDescriptor desc, List allMethods) { if (getJsonUnwrapper(method.getAnnotations()) == null) { continue; } - desc.unwrappers.add(new UnwrapperDescriptor(method)); - } - if (!desc.unwrappers.isEmpty()) { - boolean isSimpleValue = false; - for(UnwrapperDescriptor unwarpper: desc.unwrappers) { - if (unwarpper.isSimpleValue) { - if (isSimpleValue) { - throw new JsonException("Multiple @JsonUnwrapper on simple value getter"); - } - else { - isSimpleValue = true; - } + UnwrapperDescriptor unwarpper = new UnwrapperDescriptor(method); + if (unwarpper.isSimpleValue) { + if (isSimpleValue) { + throw new JsonException("Multiple @JsonUnwrapper on simple value getter"); + } + else { + isSimpleValue = true; } } + desc.unwrappers.add(unwarpper); } }