diff --git a/pom.xml b/pom.xml index b3f817c0..29fab04d 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,7 @@ + org.apache.maven.plugins maven-gpg-plugin @@ -169,6 +170,7 @@ + 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..b2f03c88 100644 --- a/src/main/java/com/jsoniter/output/CodegenImplObject.java +++ b/src/main/java/com/jsoniter/output/CodegenImplObject.java @@ -12,41 +12,57 @@ 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();"); + boolean isSimpleValue = false; + for(UnwrapperDescriptor unwarpper: desc.unwrappers) { + if (unwarpper.isSimpleValue) { + isSimpleValue = true; + break; + } } - for (EncodeTo encodeTo : encodeTos) { - notFirst = genField(ctx, encodeTo.binding, encodeTo.toName, notFirst); + 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..ec5fea82 100644 --- a/src/main/java/com/jsoniter/output/ReflectionObjectEncoder.java +++ b/src/main/java/com/jsoniter/output/ReflectionObjectEncoder.java @@ -67,42 +67,56 @@ 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 = false; + for(UnwrapperDescriptor unwarpper: desc.unwrappers) { + if (unwarpper.isSimpleValue) { + isSimpleValue = true; + break; + } } - for (EncodeTo encodeTo : getters) { - Object val = encodeTo.binding.method.invoke(obj); - notFirst = writeEncodeTo(stream, notFirst, encodeTo, val); + if (isSimpleValue) { + UnwrapperDescriptor unwrapper = desc.unwrappers.get(0); + Object simpleValue = unwrapper.method.invoke(obj); + stream.writeVal(unwrapper.mapValueTypeLiteral, simpleValue); } - 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/Config.java b/src/main/java/com/jsoniter/spi/Config.java index 5b0d6c98..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,7 +263,16 @@ private void detectUnwrappers(ClassDescriptor desc, List allMethods) { if (getJsonUnwrapper(method.getAnnotations()) == null) { continue; } - desc.unwrappers.add(new UnwrapperDescriptor(method)); + 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); } } diff --git a/src/main/java/com/jsoniter/spi/UnwrapperDescriptor.java b/src/main/java/com/jsoniter/spi/UnwrapperDescriptor.java index eb758054..d18a5976 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.getReturnType(); + 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..7ba8f228 100644 --- a/src/test/java/com/jsoniter/output/TestAnnotationJsonUnwrapper.java +++ b/src/test/java/com/jsoniter/output/TestAnnotationJsonUnwrapper.java @@ -48,4 +48,34 @@ 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 { + + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @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()); + } }