Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support simpleValue type for @JsonUnwrapper #223

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.jsoniter</groupId>
<version>0.9.23</version>
<version>0.9.24-SNAPSHOT</version>
<artifactId>jsoniter</artifactId>
<name>json iterator</name>
<description>jsoniter (json-iterator) is fast and flexible JSON parser available in Java and Go</description>
Expand Down Expand Up @@ -155,6 +155,7 @@
</execution>
</executions>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
Expand All @@ -169,6 +170,7 @@
</execution>
</executions>
</plugin>

<plugin>
<groupId>org.sonatype.plugins</groupId>
<artifactId>nexus-staging-maven-plugin</artifactId>
Expand Down
78 changes: 47 additions & 31 deletions src/main/java/com/jsoniter/output/CodegenImplObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,41 +12,57 @@ public static CodegenResult genObject(ClassInfo classInfo) {
List<EncodeTo> 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 {
Expand Down
64 changes: 39 additions & 25 deletions src/main/java/com/jsoniter/output/ReflectionObjectEncoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<Object, Object> map = (Map<Object, Object>) unwrapper.method.invoke(obj);
for (Map.Entry<Object, Object> 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<Object, Object> map = (Map<Object, Object>) unwrapper.method.invoke(obj);
for (Map.Entry<Object, Object> 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 {
Expand Down
12 changes: 11 additions & 1 deletion src/main/java/com/jsoniter/spi/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -255,14 +255,24 @@ public void updateClassDescriptor(ClassDescriptor desc) {
}

private void detectUnwrappers(ClassDescriptor desc, List<Method> allMethods) {
boolean isSimpleValue = false;
for (Method method : allMethods) {
if (Modifier.isStatic(method.getModifiers())) {
continue;
}
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);
}
}

Expand Down
9 changes: 8 additions & 1 deletion src/main/java/com/jsoniter/spi/UnwrapperDescriptor.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ public class UnwrapperDescriptor {

public boolean isMap;

public boolean isSimpleValue;

public TypeLiteral mapValueTypeLiteral;

public UnwrapperDescriptor(Method method) {
Expand All @@ -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);
}
}
Expand Down
30 changes: 30 additions & 0 deletions src/test/java/com/jsoniter/output/TestAnnotationJsonUnwrapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
}