Skip to content

Commit 7ddb55c

Browse files
committed
#82 if nullable=false, always set omitNull=false; make serialize object fields/properties deduplicated & sorted
1 parent 54ea5dd commit 7ddb55c

5 files changed

Lines changed: 98 additions & 77 deletions

File tree

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

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

3-
import com.jsoniter.CodegenAccess;
43
import com.jsoniter.spi.*;
54

65
import java.util.*;
@@ -10,27 +9,13 @@ public static CodegenResult genObject(ClassInfo classInfo) {
109

1110
CodegenResult ctx = new CodegenResult();
1211
ClassDescriptor desc = ClassDescriptor.getEncodingClassDescriptor(classInfo, false);
13-
HashMap<String, Binding> bindings = new HashMap<String, Binding>();
14-
for (Binding binding : desc.allEncoderBindings()) {
15-
for (String toName : binding.toNames) {
16-
bindings.put(toName, binding);
17-
}
18-
}
19-
ArrayList<String> toNames = new ArrayList<String>(bindings.keySet());
20-
Collections.sort(toNames, new Comparator<String>() {
21-
@Override
22-
public int compare(String o1, String o2) {
23-
int x = CodegenAccess.calcHash(o1);
24-
int y = CodegenAccess.calcHash(o2);
25-
return (x < y) ? -1 : ((x == y) ? 0 : 1);
26-
}
27-
});
12+
List<EncodeTo> encodeTos = desc.encodeTos();
2813
ctx.append(String.format("public static void encode_(%s obj, com.jsoniter.output.JsonStream stream) throws java.io.IOException {", classInfo.clazz.getCanonicalName()));
2914
if (hasFieldOutput(desc)) {
3015
int notFirst = 0;
3116
ctx.buffer('{');
32-
for (String toName : toNames) {
33-
notFirst = genField(ctx, bindings.get(toName), toName, notFirst);
17+
for (EncodeTo encodeTo : encodeTos) {
18+
notFirst = genField(ctx, encodeTo.binding, encodeTo.toName, notFirst);
3419
}
3520
for (UnwrapperDescriptor unwrapper : desc.unwrappers) {
3621
if (unwrapper.isMap) {
@@ -62,12 +47,7 @@ private static boolean hasFieldOutput(ClassDescriptor desc) {
6247
if (!desc.unwrappers.isEmpty()) {
6348
return true;
6449
}
65-
for (Binding binding : desc.allEncoderBindings()) {
66-
if (binding.toNames.length > 0) {
67-
return true;
68-
}
69-
}
70-
return false;
50+
return !desc.encodeTos().isEmpty();
7151
}
7252

7353
private static int genField(CodegenResult ctx, Binding binding, String toName, int notFirst) {

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

Lines changed: 41 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,31 @@
44
import com.jsoniter.any.Any;
55

66
import java.io.IOException;
7+
import java.lang.reflect.Field;
8+
import java.util.ArrayList;
79
import java.util.HashMap;
10+
import java.util.List;
811
import java.util.Map;
912

1013
class ReflectionObjectEncoder implements Encoder.ReflectionEncoder {
1114

1215
private final ClassDescriptor desc;
16+
private final List<EncodeTo> fields = new ArrayList<EncodeTo>();
17+
private final List<EncodeTo> getters = new ArrayList<EncodeTo>();
1318

1419
public ReflectionObjectEncoder(ClassInfo classInfo) {
1520
desc = ClassDescriptor.getEncodingClassDescriptor(classInfo, true);
16-
for (Binding binding : desc.allEncoderBindings()) {
21+
for (EncodeTo encodeTo : desc.encodeTos()) {
22+
Binding binding = encodeTo.binding;
1723
if (binding.encoder == null) {
1824
// the field encoder might be registered directly
1925
binding.encoder = JsoniterSpi.getEncoder(binding.encoderCacheKey());
2026
}
27+
if (binding.field != null) {
28+
fields.add(encodeTo);
29+
} else {
30+
getters.add(encodeTo);
31+
}
2132
}
2233
}
2334

@@ -34,17 +45,13 @@ public void encode(Object obj, JsonStream stream) throws IOException {
3445
public Any wrap(Object obj) {
3546
HashMap<String, Object> copied = new HashMap<String, Object>();
3647
try {
37-
for (Binding field : desc.fields) {
38-
Object val = field.field.get(obj);
39-
for (String toName : field.toNames) {
40-
copied.put(toName, val);
41-
}
48+
for (EncodeTo encodeTo : fields) {
49+
Object val = encodeTo.binding.field.get(obj);
50+
copied.put(encodeTo.toName, val);
4251
}
43-
for (Binding getter : desc.getters) {
44-
Object val = getter.method.invoke(obj);
45-
for (String toName : getter.toNames) {
46-
copied.put(toName, val);
47-
}
52+
for (EncodeTo getter : getters) {
53+
Object val = getter.binding.method.invoke(obj);
54+
copied.put(getter.toName, val);
4855
}
4956
} catch (Exception e) {
5057
throw new JsonException(e);
@@ -59,41 +66,13 @@ private void enocde_(Object obj, JsonStream stream) throws Exception {
5966
}
6067
stream.writeObjectStart();
6168
boolean notFirst = false;
62-
for (Binding field : desc.fields) {
63-
Object val = field.field.get(obj);
64-
for (String toName : field.toNames) {
65-
if (!(field.shouldOmitNull && val == null)) {
66-
if (notFirst) {
67-
stream.writeMore();
68-
} else {
69-
notFirst = true;
70-
}
71-
stream.writeObjectField(toName);
72-
if (field.encoder != null) {
73-
field.encoder.encode(val, stream);
74-
} else {
75-
stream.writeVal(val);
76-
}
77-
}
78-
}
69+
for (EncodeTo encodeTo : fields) {
70+
Object val = encodeTo.binding.field.get(obj);
71+
notFirst = writeEncodeTo(stream, notFirst, encodeTo, val);
7972
}
80-
for (Binding getter : desc.getters) {
81-
Object val = getter.method.invoke(obj);
82-
for (String toName : getter.toNames) {
83-
if (!(getter.shouldOmitNull && val == null)) {
84-
if (notFirst) {
85-
stream.writeMore();
86-
} else {
87-
notFirst = true;
88-
}
89-
stream.writeObjectField(toName);
90-
if (getter.encoder != null) {
91-
getter.encoder.encode(val, stream);
92-
} else {
93-
stream.writeVal(val);
94-
}
95-
}
96-
}
73+
for (EncodeTo encodeTo : getters) {
74+
Object val = encodeTo.binding.method.invoke(obj);
75+
notFirst = writeEncodeTo(stream, notFirst, encodeTo, val);
9776
}
9877
for (UnwrapperDescriptor unwrapper : desc.unwrappers) {
9978
if (unwrapper.isMap) {
@@ -118,4 +97,21 @@ private void enocde_(Object obj, JsonStream stream) throws Exception {
11897
}
11998
stream.writeObjectEnd();
12099
}
100+
101+
private boolean writeEncodeTo(JsonStream stream, boolean notFirst, EncodeTo encodeTo, Object val) throws IOException {
102+
if (!(encodeTo.binding.shouldOmitNull && val == null)) {
103+
if (notFirst) {
104+
stream.writeMore();
105+
} else {
106+
notFirst = true;
107+
}
108+
stream.writeObjectField(encodeTo.toName);
109+
if (encodeTo.binding.encoder != null) {
110+
encodeTo.binding.encoder.encode(val, stream);
111+
} else {
112+
stream.writeVal(val);
113+
}
114+
}
115+
return notFirst;
116+
}
121117
}

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

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,9 @@ public static ClassDescriptor getEncodingClassDescriptor(ClassInfo classInfo, bo
107107
if (binding.encoder != null) {
108108
JsoniterSpi.addNewEncoder(binding.encoderCacheKey(), binding.encoder);
109109
}
110+
if (!binding.isNullable) {
111+
binding.shouldOmitNull = false;
112+
}
110113
}
111114
return desc;
112115
}
@@ -423,10 +426,40 @@ public List<Binding> allDecoderBindings() {
423426
return bindings;
424427
}
425428

429+
426430
public List<Binding> allEncoderBindings() {
427431
ArrayList<Binding> bindings = new ArrayList<Binding>(8);
428432
bindings.addAll(fields);
429433
bindings.addAll(getters);
430434
return bindings;
431435
}
436+
437+
public List<EncodeTo> encodeTos() {
438+
HashMap<String, Integer> previousAppearance = new HashMap<String, Integer>();
439+
ArrayList<EncodeTo> encodeTos = new ArrayList<EncodeTo>(8);
440+
collectEncodeTo(encodeTos, fields, previousAppearance);
441+
collectEncodeTo(encodeTos, getters, previousAppearance);
442+
ArrayList<EncodeTo> removedNulls = new ArrayList<EncodeTo>(encodeTos.size());
443+
for (EncodeTo encodeTo : encodeTos) {
444+
if (encodeTo != null) {
445+
removedNulls.add(encodeTo);
446+
}
447+
}
448+
return removedNulls;
449+
}
450+
451+
private void collectEncodeTo(ArrayList<EncodeTo> encodeTos, List<Binding> fields, HashMap<String, Integer> previousAppearance) {
452+
for (Binding field : fields) {
453+
for (String toName : field.toNames) {
454+
if (previousAppearance.containsKey(toName)) {
455+
encodeTos.set(previousAppearance.get(toName), null);
456+
}
457+
previousAppearance.put(toName, encodeTos.size());
458+
EncodeTo encodeTo = new EncodeTo();
459+
encodeTo.binding = field;
460+
encodeTo.toName = toName;
461+
encodeTos.add(encodeTo);
462+
}
463+
}
464+
}
432465
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.jsoniter.spi;
2+
3+
public class EncodeTo {
4+
public Binding binding;
5+
public String toName;
6+
}

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

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.jsoniter.annotation.JsonIgnore;
44
import com.jsoniter.annotation.JsonProperty;
55
import com.jsoniter.spi.Config;
6+
import com.jsoniter.spi.JsonException;
67
import com.jsoniter.spi.JsoniterSpi;
78
import com.jsoniter.spi.TypeLiteral;
89
import junit.framework.TestCase;
@@ -219,21 +220,21 @@ public void test_not_omit_null() {
219220
public static class TestObject11 {
220221
public String field1;
221222
public String field2;
222-
public String field3;
223+
@JsonProperty(nullable = false)
224+
public Integer field3;
223225
}
224226

225227
public void test_omit_null() {
226-
// JsonStream.setMode(EncodingMode.DYNAMIC_MODE);
227-
assertEquals("{}", JsonStream.serialize(new TestObject11()));
228+
assertEquals("{\"field3\":null}", JsonStream.serialize(new TestObject11()));
228229
TestObject11 obj = new TestObject11();
229230
obj.field1 = "hello";
230-
assertEquals("{\"field1\":\"hello\"}", JsonStream.serialize(obj));
231+
assertEquals("{\"field1\":\"hello\",\"field3\":null}", JsonStream.serialize(obj));
231232
obj = new TestObject11();
232233
obj.field2 = "hello";
233-
assertEquals("{\"field2\":\"hello\"}", JsonStream.serialize(obj));
234+
assertEquals("{\"field2\":\"hello\",\"field3\":null}", JsonStream.serialize(obj));
234235
obj = new TestObject11();
235-
obj.field3 = "hello";
236-
assertEquals("{\"field3\":\"hello\"}", JsonStream.serialize(obj));
236+
obj.field3 = 3;
237+
assertEquals("{\"field3\":3}", JsonStream.serialize(obj));
237238
}
238239

239240

@@ -260,6 +261,11 @@ public void test_private_class() {
260261
if (EncodingMode.REFLECTION_MODE.equals(encodingMode)) {
261262
return;
262263
}
263-
JsonStream.serialize(new TestObject13());
264+
try {
265+
JsonStream.serialize(new TestObject13());
266+
fail("should throw JsonException");
267+
} catch (JsonException e) {
268+
269+
}
264270
}
265271
}

0 commit comments

Comments
 (0)