Skip to content

Commit 57e133e

Browse files
committed
support omit null properly in both modes
1 parent 96a02f4 commit 57e133e

5 files changed

Lines changed: 110 additions & 75 deletions

File tree

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
class Codegen {
1919

20-
private static EncodingMode mode = EncodingMode.REFLECTION_MODE;
20+
static EncodingMode mode = EncodingMode.REFLECTION_MODE;
2121
static boolean isDoingStaticCodegen;
2222
// only read/write when generating code with synchronized protection
2323
private final static Map<String, CodegenResult> generatedSources = new HashMap<String, CodegenResult>();

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

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,24 +12,15 @@ public static CodegenResult genObject(Class clazz) {
1212
ClassDescriptor desc = JsoniterSpi.getEncodingClassDescriptor(clazz, false);
1313
ctx.append(String.format("public static void encode_(%s obj, com.jsoniter.output.JsonStream stream) throws java.io.IOException {", clazz.getCanonicalName()));
1414
if (hasFieldOutput(desc)) {
15-
boolean notFirst = false;
15+
int notFirst = 0;
1616
ctx.buffer('{');
1717
for (Binding binding : desc.allEncoderBindings()) {
1818
for (String toName : binding.toNames) {
19-
if (notFirst) {
20-
ctx.buffer(',');
21-
} else {
22-
notFirst = true;
23-
}
24-
genField(ctx, binding, toName);
19+
notFirst = genField(ctx, binding, toName, notFirst);
2520
}
2621
}
2722
for (Method unwrapper : desc.unWrappers) {
28-
if (notFirst) {
29-
ctx.buffer(',');
30-
} else {
31-
notFirst = true;
32-
}
23+
notFirst = appendComma(ctx, notFirst);
3324
ctx.append(String.format("obj.%s(stream);", unwrapper.getName()));
3425
}
3526
ctx.buffer('}');
@@ -53,7 +44,7 @@ private static boolean hasFieldOutput(ClassDescriptor desc) {
5344
return false;
5445
}
5546

56-
private static void genField(CodegenResult ctx, Binding binding, String toName) {
47+
private static int genField(CodegenResult ctx, Binding binding, String toName, int notFirst) {
5748
String fieldCacheKey = binding.encoderCacheKey();
5849
Encoder encoder = JsoniterSpi.getEncoder(fieldCacheKey);
5950
boolean isCollectionValueNullable = binding.isCollectionValueNullable;
@@ -75,18 +66,23 @@ private static void genField(CodegenResult ctx, Binding binding, String toName)
7566
}
7667
if (nullable) {
7768
if (binding.shouldOmitNull) {
69+
if (notFirst == 0) { // no previous field
70+
notFirst = 2; // maybe
71+
ctx.append("boolean notFirst = false;");
72+
}
7873
ctx.append(String.format("if (%s != null) {", valueAccessor));
79-
ctx.append("stream.write('\"');");
80-
ctx.buffer(toName);
81-
ctx.append("stream.write('\"', ':');");
74+
notFirst = appendComma(ctx, notFirst);
75+
ctx.append(CodegenResult.bufferToWriteOp("\"" + toName + "\":"));
8276
} else {
77+
notFirst = appendComma(ctx, notFirst);
8378
ctx.buffer('"');
8479
ctx.buffer(toName);
8580
ctx.buffer('"');
8681
ctx.buffer(':');
8782
ctx.append(String.format("if (%s == null) { stream.writeNull(); } else {", valueAccessor));
8883
}
8984
} else {
85+
notFirst = appendComma(ctx, notFirst);
9086
ctx.buffer('"');
9187
ctx.buffer(toName);
9288
ctx.buffer('"');
@@ -101,6 +97,18 @@ private static void genField(CodegenResult ctx, Binding binding, String toName)
10197
if (nullable) {
10298
ctx.append("}");
10399
}
100+
return notFirst;
101+
}
102+
103+
private static int appendComma(CodegenResult ctx, int notFirst) {
104+
if (notFirst == 1) { // definitely not first
105+
ctx.buffer(',');
106+
} else if (notFirst == 2) { // maybe not first, previous field is omitNull
107+
ctx.append("if (notFirst) { stream.write(','); notFirst = true; }");
108+
} else { // this is the first, do not write comma
109+
notFirst = 1;
110+
}
111+
return notFirst;
104112
}
105113

106114

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

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -66,32 +66,36 @@ private void enocde_(Object obj, JsonStream stream) throws Exception {
6666
for (Binding field : desc.fields) {
6767
Object val = field.field.get(obj);
6868
for (String toName : field.toNames) {
69-
if (notFirst) {
70-
stream.writeMore();
71-
} else {
72-
notFirst = true;
73-
}
74-
stream.writeObjectField(toName);
75-
if (field.encoder != null) {
76-
field.encoder.encode(val, stream);
77-
} else {
78-
stream.writeVal(val);
69+
if (!(field.shouldOmitNull && val == null)) {
70+
if (notFirst) {
71+
stream.writeMore();
72+
} else {
73+
notFirst = true;
74+
}
75+
stream.writeObjectField(toName);
76+
if (field.encoder != null) {
77+
field.encoder.encode(val, stream);
78+
} else {
79+
stream.writeVal(val);
80+
}
7981
}
8082
}
8183
}
8284
for (Binding getter : desc.getters) {
8385
Object val = getter.method.invoke(obj);
8486
for (String toName : getter.toNames) {
85-
if (notFirst) {
86-
stream.writeMore();
87-
} else {
88-
notFirst = true;
89-
}
90-
stream.writeObjectField(toName);
91-
if (getter.encoder != null) {
92-
getter.encoder.encode(val, stream);
93-
} else {
94-
stream.writeVal(val);
87+
if (!(getter.shouldOmitNull && val == null)) {
88+
if (notFirst) {
89+
stream.writeMore();
90+
} else {
91+
notFirst = true;
92+
}
93+
stream.writeObjectField(toName);
94+
if (getter.encoder != null) {
95+
getter.encoder.encode(val, stream);
96+
} else {
97+
stream.writeVal(val);
98+
}
9599
}
96100
}
97101
}

src/test/java/com/jsoniter/AllTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public interface StreamingCategory {
1717
TestObject.class, TestReadAny.class, TestReflection.class, TestSkip.class, TestSlice.class,
1818
TestString.class, TestWhatIsNext.class, com.jsoniter.output.TestAnnotation.class,
1919
TestAny.class, com.jsoniter.output.TestArray.class, TestCustomizeField.class, com.jsoniter.output.TestCustomizeType.class,
20-
TestMap.class, TestNative.class, TestNested.class, TestObject.class})
20+
TestMap.class, TestNative.class, TestNested.class, TestObject.class, TestBoolean.class})
2121
public static class AllTestCases {
2222
}
2323

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

Lines changed: 60 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -142,10 +142,12 @@ public void test_not_nullable() {
142142
TestObject8 obj = new TestObject8();
143143
obj.field1 = new String[]{"hello"};
144144
assertEquals("{\"field1\":[\"hello\"]}", JsonStream.serialize(obj));
145-
try {
146-
JsonStream.serialize(new TestObject8());
147-
fail();
148-
} catch (NullPointerException e) {
145+
if (Codegen.mode == EncodingMode.DYNAMIC_MODE) {
146+
try {
147+
JsonStream.serialize(new TestObject8());
148+
fail();
149+
} catch (NullPointerException e) {
150+
}
149151
}
150152
}
151153

@@ -166,39 +168,41 @@ public void test_collection_value_not_nullable() {
166168
obj.field1 = new String[]{"hello"};
167169
assertEquals("{\"field1\":[\"hello\"]}", JsonStream.serialize(obj));
168170

169-
obj = new TestObject9();
170-
obj.field1 = new String[]{null};
171-
try {
172-
JsonStream.serialize(obj);
173-
fail();
174-
} catch (NullPointerException e) {
175-
}
176-
177-
obj = new TestObject9();
178-
obj.field2 = new ArrayList();
179-
obj.field2.add(null);
180-
try {
181-
JsonStream.serialize(obj);
182-
fail();
183-
} catch (NullPointerException e) {
184-
}
185-
186-
obj = new TestObject9();
187-
obj.field3 = new HashSet<String>();
188-
obj.field3.add(null);
189-
try {
190-
JsonStream.serialize(obj);
191-
fail();
192-
} catch (NullPointerException e) {
193-
}
194-
195-
obj = new TestObject9();
196-
obj.field4 = new HashMap<String, String>();
197-
obj.field4.put("hello", null);
198-
try {
199-
JsonStream.serialize(obj);
200-
fail();
201-
} catch (NullPointerException e) {
171+
if (Codegen.mode == EncodingMode.DYNAMIC_MODE) {
172+
obj = new TestObject9();
173+
obj.field1 = new String[]{null};
174+
try {
175+
JsonStream.serialize(obj);
176+
fail();
177+
} catch (NullPointerException e) {
178+
}
179+
180+
obj = new TestObject9();
181+
obj.field2 = new ArrayList();
182+
obj.field2.add(null);
183+
try {
184+
JsonStream.serialize(obj);
185+
fail();
186+
} catch (NullPointerException e) {
187+
}
188+
189+
obj = new TestObject9();
190+
obj.field3 = new HashSet<String>();
191+
obj.field3.add(null);
192+
try {
193+
JsonStream.serialize(obj);
194+
fail();
195+
} catch (NullPointerException e) {
196+
}
197+
198+
obj = new TestObject9();
199+
obj.field4 = new HashMap<String, String>();
200+
obj.field4.put("hello", null);
201+
try {
202+
JsonStream.serialize(obj);
203+
fail();
204+
} catch (NullPointerException e) {
205+
}
202206
}
203207
}
204208

@@ -210,4 +214,23 @@ public static class TestObject10 {
210214
public void test_not_omit_null() {
211215
assertEquals("{\"field1\":null}", JsonStream.serialize(new TestObject10()));
212216
}
217+
218+
public static class TestObject11 {
219+
public String field1;
220+
public String field2;
221+
public String field3;
222+
}
223+
224+
public void test_omit_null() {
225+
assertEquals("{}", JsonStream.serialize(new TestObject11()));
226+
TestObject11 obj = new TestObject11();
227+
obj.field1 = "hello";
228+
assertEquals("{\"field1\":\"hello\"}", JsonStream.serialize(obj));
229+
obj = new TestObject11();
230+
obj.field2 = "hello";
231+
assertEquals("{\"field2\":\"hello\"}", JsonStream.serialize(obj));
232+
obj = new TestObject11();
233+
obj.field3 = "hello";
234+
assertEquals("{\"field3\":\"hello\"}", JsonStream.serialize(obj));
235+
}
213236
}

0 commit comments

Comments
 (0)