Skip to content

Commit 2a84f24

Browse files
committed
support unwrapper
1 parent 7ebbad4 commit 2a84f24

10 files changed

Lines changed: 97 additions & 31 deletions

File tree

src/main/java/com/jsoniter/CodegenImplObject.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ public static String genObjectUsingStrict(Class clazz, ClassDescriptor desc) {
127127
append(lines, String.format("obj.%s(_%s_);", setter.method.getName(), setter.name));
128128
}
129129
}
130-
appendSetter(desc.wrappers, lines);
130+
appendWrappers(desc.wrappers, lines);
131131
append(lines, "return obj;");
132132
return lines.toString()
133133
.replace("{{clazz}}", clazz.getCanonicalName())
@@ -418,7 +418,7 @@ public static String genObjectUsingHash(Class clazz, ClassDescriptor desc) {
418418
append(lines, String.format("obj.%s(_%s_);", setter.method.getName(), setter.name));
419419
}
420420
}
421-
appendSetter(desc.wrappers, lines);
421+
appendWrappers(desc.wrappers, lines);
422422
append(lines, "return obj;");
423423
return lines.toString()
424424
.replace("{{clazz}}", clazz.getCanonicalName())
@@ -440,11 +440,11 @@ private static void appendBindingSet(StringBuilder lines, ClassDescriptor desc,
440440
}
441441
}
442442

443-
private static void appendSetter(List<WrapperDescriptor> setters, StringBuilder lines) {
444-
for (WrapperDescriptor setter : setters) {
443+
private static void appendWrappers(List<WrapperDescriptor> wrappers, StringBuilder lines) {
444+
for (WrapperDescriptor wrapper : wrappers) {
445445
lines.append("obj.");
446-
lines.append(setter.methodName);
447-
appendInvocation(lines, setter.parameters);
446+
lines.append(wrapper.method.getName());
447+
appendInvocation(lines, wrapper.parameters);
448448
lines.append(";\n");
449449
}
450450
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.jsoniter.annotation;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Retention;
5+
import java.lang.annotation.RetentionPolicy;
6+
import java.lang.annotation.Target;
7+
8+
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
9+
@Retention(RetentionPolicy.RUNTIME)
10+
public @interface JsonUnwrapper {
11+
}

src/main/java/com/jsoniter/annotation/JsoniterAnnotationSupport.java

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,33 @@ public void updateClassDescriptor(ClassDescriptor desc) {
4141
desc.fields.add(binding);
4242
}
4343
}
44+
List<Method> allMethods = new ArrayList<Method>();
45+
Class current = desc.clazz;
46+
while (current != null) {
47+
allMethods.addAll(Arrays.asList(current.getDeclaredMethods()));
48+
current = current.getSuperclass();
49+
}
4450
updateBindings(desc);
45-
detectCtorBinding(desc);
46-
detectStaticFactoryBinding(desc);
47-
detectWrapperBinding(desc);
51+
detectCtor(desc);
52+
detectStaticFactory(desc, allMethods);
53+
detectWrappers(desc, allMethods);
54+
detectUnwrappers(desc, allMethods);
55+
}
56+
57+
private void detectUnwrappers(ClassDescriptor desc, List<Method> allMethods) {
58+
for (Method method : allMethods) {
59+
if (Modifier.isStatic(method.getModifiers())) {
60+
continue;
61+
}
62+
if (method.getAnnotation(JsonUnwrapper.class) == null) {
63+
continue;
64+
}
65+
desc.unwrappers.add(method);
66+
}
4867
}
4968

50-
private void detectWrapperBinding(ClassDescriptor desc) {
51-
for (Method method : desc.clazz.getMethods()) {
69+
private void detectWrappers(ClassDescriptor desc, List<Method> allMethods) {
70+
for (Method method : allMethods) {
5271
if (Modifier.isStatic(method.getModifiers())) {
5372
continue;
5473
}
@@ -58,7 +77,6 @@ private void detectWrapperBinding(ClassDescriptor desc) {
5877
Annotation[][] annotations = method.getParameterAnnotations();
5978
String[] paramNames = getParamNames(method, annotations.length);
6079
WrapperDescriptor setter = new WrapperDescriptor();
61-
setter.methodName = method.getName();
6280
setter.method = method;
6381
for (int i = 0; i < annotations.length; i++) {
6482
Annotation[] paramAnnotations = annotations[i];
@@ -97,13 +115,7 @@ private Object reflectCall(Object obj, String methodName, Object... args) throws
97115
return method.invoke(obj, args);
98116
}
99117

100-
private void detectStaticFactoryBinding(ClassDescriptor desc) {
101-
List<Method> allMethods = new ArrayList<Method>();
102-
Class current = desc.clazz;
103-
while (current != null) {
104-
allMethods.addAll(Arrays.asList(current.getDeclaredMethods()));
105-
current = current.getSuperclass();
106-
}
118+
private void detectStaticFactory(ClassDescriptor desc, List<Method> allMethods) {
107119
for (Method method : allMethods) {
108120
if (!Modifier.isStatic(method.getModifiers())) {
109121
continue;
@@ -136,7 +148,7 @@ private void detectStaticFactoryBinding(ClassDescriptor desc) {
136148
}
137149
}
138150

139-
private void detectCtorBinding(ClassDescriptor desc) {
151+
private void detectCtor(ClassDescriptor desc) {
140152
for (Constructor ctor : desc.clazz.getDeclaredConstructors()) {
141153
JsonCreator jsonCreator = getJsonCreator(ctor.getAnnotations());
142154
if (jsonCreator == null) {

src/main/java/com/jsoniter/output/CodegenImplObject.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import com.jsoniter.spi.Encoder;
66
import com.jsoniter.spi.JsoniterSpi;
77

8+
import java.lang.reflect.Method;
9+
810
class CodegenImplObject {
911
public static String genObject(Class clazz) {
1012
ClassDescriptor desc = JsoniterSpi.getEncodingClassDescriptor(clazz, false);
@@ -25,6 +27,9 @@ public static String genObject(Class clazz) {
2527
append(lines, genField(field));
2628
}
2729
}
30+
for (Method unwrapper : desc.unwrappers) {
31+
append(lines, String.format("obj.%s(stream);", unwrapper.getName()));
32+
}
2833
append(lines, "stream.writeObjectEnd();");
2934
} else {
3035
append(lines, "stream.writeEmptyObject();");
@@ -34,6 +39,9 @@ public static String genObject(Class clazz) {
3439
}
3540

3641
private static boolean hasFieldOutput(ClassDescriptor desc) {
42+
if (!desc.unwrappers.isEmpty()) {
43+
return true;
44+
}
3745
for (Binding binding : desc.allEncoderBindings()) {
3846
if (binding.toNames.length > 0) {
3947
return true;

src/main/java/com/jsoniter/output/ReflectionObjectEncoder.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,20 @@
77
import com.jsoniter.spi.JsoniterSpi;
88

99
import java.io.IOException;
10+
import java.lang.reflect.Method;
1011

1112
class ReflectionObjectEncoder implements Encoder {
1213

1314
private final ClassDescriptor desc;
1415

1516
public ReflectionObjectEncoder(Class clazz) {
1617
desc = JsoniterSpi.getEncodingClassDescriptor(clazz, true);
18+
for (Binding binding : desc.allEncoderBindings()) {
19+
if (binding.encoder == null) {
20+
// the field encoder might be registered directly
21+
binding.encoder = JsoniterSpi.getEncoder(binding.encoderCacheKey());
22+
}
23+
}
1724
}
1825

1926
@Override
@@ -41,7 +48,11 @@ private void enocde_(Object obj, JsonStream stream) throws Exception {
4148
notFirst = true;
4249
}
4350
stream.writeObjectField(toName);
44-
stream.writeVal(val);
51+
if (field.encoder != null) {
52+
field.encoder.encode(val, stream);
53+
} else {
54+
stream.writeVal(val);
55+
}
4556
}
4657
}
4758
for (Binding getter : desc.getters) {
@@ -53,9 +64,16 @@ private void enocde_(Object obj, JsonStream stream) throws Exception {
5364
notFirst = true;
5465
}
5566
stream.writeObjectField(toName);
56-
stream.writeVal(val);
67+
if (getter.encoder != null) {
68+
getter.encoder.encode(val, stream);
69+
} else {
70+
stream.writeVal(val);
71+
}
5772
}
5873
}
74+
for (Method unwrapper : desc.unwrappers) {
75+
unwrapper.invoke(obj, stream);
76+
}
5977
stream.writeObjectEnd();
6078
}
6179
}

src/main/java/com/jsoniter/spi/Binding.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ public class Binding {
99
public final Class clazz;
1010
public final TypeLiteral clazzTypeLiteral;
1111
public Annotation[] annotations;
12+
public Field field; // obj.XXX
13+
public Method method; // obj.setXXX() or obj.getXXX()
14+
public boolean valueCanReuse;
1215
// input/output
1316
public String name;
1417
public Type valueType;
@@ -23,12 +26,9 @@ public class Binding {
2326
// then this property will not be unknown
2427
// but we do not want to bind it anywhere
2528
public boolean shouldSkip;
26-
// optional
27-
public Field field; // obj.XXX
28-
public Method method; // obj.setXXX() or obj.getXXX()
29+
// attachment, used when generating code or reflection
2930
public int idx;
3031
public long mask;
31-
public boolean valueCanReuse;
3232

3333
public Binding(Class clazz, Map<String, Type> lookup, Type valueType) {
3434
this.clazz = clazz;

src/main/java/com/jsoniter/spi/ClassDescriptor.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.jsoniter.spi;
22

3+
import java.lang.reflect.Method;
34
import java.lang.reflect.Type;
45
import java.util.ArrayList;
56
import java.util.List;
@@ -14,6 +15,7 @@ public class ClassDescriptor {
1415
public List<Binding> setters;
1516
public List<Binding> getters;
1617
public List<WrapperDescriptor> wrappers;
18+
public List<Method> unwrappers;
1719
public boolean asExtraForUnknownProperties;
1820
public Binding onMissingProperties;
1921
public Binding onExtraProperties;

src/main/java/com/jsoniter/spi/JsoniterSpi.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ public static ClassDescriptor getDecodingClassDescriptor(Class clazz, boolean in
8989
desc.fields = getFields(lookup, clazz, includingPrivate);
9090
desc.setters = getSetters(lookup, clazz, includingPrivate);
9191
desc.wrappers = new ArrayList<WrapperDescriptor>();
92+
desc.unwrappers = new ArrayList<Method>();
9293
for (Extension extension : extensions) {
9394
extension.updateClassDescriptor(desc);
9495
}
@@ -138,6 +139,8 @@ public static ClassDescriptor getEncodingClassDescriptor(Class clazz, boolean in
138139
desc.lookup = lookup;
139140
desc.fields = getFields(lookup, clazz, includingPrivate);
140141
desc.getters = getGetters(lookup, clazz, includingPrivate);
142+
desc.wrappers = new ArrayList<WrapperDescriptor>();
143+
desc.unwrappers = new ArrayList<Method>();
141144
for (Extension extension : extensions) {
142145
extension.updateClassDescriptor(desc);
143146
}

src/main/java/com/jsoniter/spi/WrapperDescriptor.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,11 @@
55
import java.util.List;
66

77
public class WrapperDescriptor {
8-
/**
9-
* which method to call to set value
10-
*/
11-
public String methodName;
128

139
/**
1410
* the parameters to bind
1511
*/
1612
public List<Binding> parameters = new ArrayList<Binding>();
1713

18-
// optional
1914
public Method method;
2015
}

src/test/java/com/jsoniter/output/TestAnnotation.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.jsoniter.annotation.JsonIgnore;
44
import com.jsoniter.annotation.JsonProperty;
5+
import com.jsoniter.annotation.JsonUnwrapper;
56
import com.jsoniter.annotation.JsoniterAnnotationSupport;
67
import com.jsoniter.spi.Encoder;
78
import junit.framework.TestCase;
@@ -12,6 +13,7 @@
1213
public class TestAnnotation extends TestCase {
1314
static {
1415
JsoniterAnnotationSupport.enable();
16+
// JsonStream.setMode(EncodingMode.REFLECTION_MODE);
1517
}
1618

1719
private ByteArrayOutputStream baos;
@@ -75,4 +77,19 @@ public void test_name_conflict() throws IOException {
7577
stream.close();
7678
assertEquals("{\"field1\":0}", baos.toString());
7779
}
80+
81+
public static class TestObject5 {
82+
@JsonUnwrapper
83+
public void unwrap(JsonStream stream) throws IOException {
84+
stream.writeObjectField("hello");
85+
stream.writeVal("world");
86+
}
87+
}
88+
89+
public void test_unwrapper() throws IOException {
90+
TestObject5 obj = new TestObject5();
91+
stream.writeVal(obj);
92+
stream.close();
93+
assertEquals("{\"hello\":\"world\"}", baos.toString());
94+
}
7895
}

0 commit comments

Comments
 (0)