Skip to content

Commit 29b76ce

Browse files
committed
Merge branch 'caoli5288-pull/add-omit-zero'
2 parents 7cdbed6 + cb9b607 commit 29b76ce

13 files changed

Lines changed: 400 additions & 66 deletions

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,11 @@
6060
boolean collectionValueNullable() default true;
6161

6262
/**
63-
* @return if true, do not write the field altogether if value is null
63+
* @return the default value to omit
64+
* null, to omit null value
65+
* \"xxx\", to omit string value
66+
* 123, to omit number
67+
* void, to always encode this field, ignore global config
6468
*/
65-
boolean omitNull() default true;
69+
String defaultValueToOmit() default "";
6670
}

src/main/java/com/jsoniter/extra/GsonCompatibilityMode.java

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ protected Builder builder() {
6565

6666
public static class Builder extends Config.Builder {
6767
private boolean excludeFieldsWithoutExposeAnnotation = false;
68-
private boolean serializeNulls = false;
6968
private boolean disableHtmlEscaping = false;
7069
private ThreadLocal<DateFormat> dateFormat = new ThreadLocal<DateFormat>() {
7170
@Override
@@ -78,13 +77,17 @@ protected DateFormat initialValue() {
7877
private Set<ExclusionStrategy> serializationExclusionStrategies = new HashSet<ExclusionStrategy>();
7978
private Set<ExclusionStrategy> deserializationExclusionStrategies = new HashSet<ExclusionStrategy>();
8079

80+
public Builder() {
81+
omitDefaultValue(true);
82+
}
83+
8184
public Builder excludeFieldsWithoutExposeAnnotation() {
8285
excludeFieldsWithoutExposeAnnotation = true;
8386
return this;
8487
}
8588

8689
public Builder serializeNulls() {
87-
serializeNulls = true;
90+
omitDefaultValue(false);
8891
return this;
8992
}
9093

@@ -174,7 +177,6 @@ public boolean equals(Object o) {
174177
Builder builder = (Builder) o;
175178

176179
if (excludeFieldsWithoutExposeAnnotation != builder.excludeFieldsWithoutExposeAnnotation) return false;
177-
if (serializeNulls != builder.serializeNulls) return false;
178180
if (disableHtmlEscaping != builder.disableHtmlEscaping) return false;
179181
if (!dateFormat.get().equals(builder.dateFormat.get())) return false;
180182
if (fieldNamingStrategy != null ? !fieldNamingStrategy.equals(builder.fieldNamingStrategy) : builder.fieldNamingStrategy != null)
@@ -189,7 +191,6 @@ public boolean equals(Object o) {
189191
public int hashCode() {
190192
int result = super.hashCode();
191193
result = 31 * result + (excludeFieldsWithoutExposeAnnotation ? 1 : 0);
192-
result = 31 * result + (serializeNulls ? 1 : 0);
193194
result = 31 * result + (disableHtmlEscaping ? 1 : 0);
194195
result = 31 * result + dateFormat.get().hashCode();
195196
result = 31 * result + (fieldNamingStrategy != null ? fieldNamingStrategy.hashCode() : 0);
@@ -203,7 +204,6 @@ public int hashCode() {
203204
public Config.Builder copy() {
204205
Builder copied = (Builder) super.copy();
205206
copied.excludeFieldsWithoutExposeAnnotation = excludeFieldsWithoutExposeAnnotation;
206-
copied.serializeNulls = serializeNulls;
207207
copied.disableHtmlEscaping = disableHtmlEscaping;
208208
copied.dateFormat = dateFormat;
209209
copied.fieldNamingStrategy = fieldNamingStrategy;
@@ -214,6 +214,17 @@ public Config.Builder copy() {
214214
}
215215
}
216216

217+
@Override
218+
protected OmitValue createOmitValue(Type valueType) {
219+
if (valueType instanceof Class) {
220+
Class clazz = (Class) valueType;
221+
if (clazz.isPrimitive()) {
222+
return null; // gson do not omit primitive zero
223+
}
224+
}
225+
return super.createOmitValue(valueType);
226+
}
227+
217228
@Override
218229
public Encoder createEncoder(String cacheKey, Type type) {
219230
if (Date.class == type) {
@@ -459,11 +470,6 @@ public void updateClassDescriptor(ClassDescriptor desc) {
459470
}
460471
}
461472
}
462-
for (Binding binding : desc.allEncoderBindings()) {
463-
if (builder().serializeNulls) {
464-
binding.shouldOmitNull = false;
465-
}
466-
}
467473
super.updateClassDescriptor(desc);
468474
}
469475

@@ -526,8 +532,8 @@ public boolean collectionValueNullable() {
526532
}
527533

528534
@Override
529-
public boolean omitNull() {
530-
return true;
535+
public String defaultValueToOmit() {
536+
return "";
531537
}
532538

533539
@Override

src/main/java/com/jsoniter/extra/JacksonCompatibilityMode.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,8 @@ public boolean collectionValueNullable() {
113113
}
114114

115115
@Override
116-
public boolean omitNull() {
117-
return true;
116+
public String defaultValueToOmit() {
117+
return "";
118118
}
119119

120120
@Override

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

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -82,33 +82,22 @@ private static int genField(CodegenResult ctx, Binding binding, String toName, i
8282
isCollectionValueNullable = true;
8383
}
8484
boolean nullable = !valueClazz.isPrimitive();
85+
boolean omitZero = JsoniterSpi.getCurrentConfig().omitDefaultValue();
8586
if (!binding.isNullable) {
8687
nullable = false;
8788
}
88-
if (nullable) {
89-
if (binding.shouldOmitNull) {
90-
if (notFirst == 0) { // no previous field
91-
notFirst = 2; // maybe
92-
ctx.append("boolean notFirst = false;");
93-
}
94-
ctx.append(String.format("if (%s != null) {", valueAccessor));
95-
notFirst = appendComma(ctx, notFirst);
96-
if (noIndention) {
97-
ctx.append(CodegenResult.bufferToWriteOp("\"" + toName + "\":"));
98-
} else {
99-
ctx.append(String.format("stream.writeObjectField(\"%s\");", toName));
100-
}
89+
if (binding.defaultValueToOmit != null) {
90+
if (notFirst == 0) { // no previous field
91+
notFirst = 2; // maybe
92+
ctx.append("boolean notFirst = false;");
93+
}
94+
95+
ctx.append("if (!(" + String.format(binding.defaultValueToOmit.code(), valueAccessor)+ ")) {");
96+
notFirst = appendComma(ctx, notFirst);
97+
if (noIndention) {
98+
ctx.append(CodegenResult.bufferToWriteOp("\"" + toName + "\":"));
10199
} else {
102-
notFirst = appendComma(ctx, notFirst);
103-
if (noIndention) {
104-
ctx.buffer('"');
105-
ctx.buffer(toName);
106-
ctx.buffer('"');
107-
ctx.buffer(':');
108-
} else {
109-
ctx.append(String.format("stream.writeObjectField(\"%s\");", toName));
110-
}
111-
ctx.append(String.format("if (%s == null) { stream.writeNull(); } else {", valueAccessor));
100+
ctx.append(String.format("stream.writeObjectField(\"%s\");", toName));
112101
}
113102
} else {
114103
notFirst = appendComma(ctx, notFirst);
@@ -120,14 +109,17 @@ private static int genField(CodegenResult ctx, Binding binding, String toName, i
120109
} else {
121110
ctx.append(String.format("stream.writeObjectField(\"%s\");", toName));
122111
}
112+
if (nullable) {
113+
ctx.append(String.format("if (%s == null) { stream.writeNull(); } else {", valueAccessor));
114+
}
123115
}
124116
if (encoder == null) {
125117
CodegenImplNative.genWriteOp(ctx, valueAccessor, binding.valueType, nullable, isCollectionValueNullable);
126118
} else {
127119
ctx.append(String.format("com.jsoniter.output.CodegenAccess.writeVal(\"%s\", %s, stream);",
128120
fieldCacheKey, valueAccessor));
129121
}
130-
if (nullable) {
122+
if (nullable || omitZero) {
131123
ctx.append("}");
132124
}
133125
return notFirst;

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import com.jsoniter.any.Any;
55

66
import java.io.IOException;
7-
import java.lang.reflect.Field;
87
import java.util.ArrayList;
98
import java.util.HashMap;
109
import java.util.List;
@@ -103,7 +102,8 @@ private void enocde_(Object obj, JsonStream stream) throws Exception {
103102
}
104103

105104
private boolean writeEncodeTo(JsonStream stream, boolean notFirst, EncodeTo encodeTo, Object val) throws IOException {
106-
if (!(encodeTo.binding.shouldOmitNull && val == null)) {
105+
OmitValue defaultValueToOmit = encodeTo.binding.defaultValueToOmit;
106+
if (!(defaultValueToOmit != null && defaultValueToOmit.shouldOmit(val))) {
107107
if (notFirst) {
108108
stream.writeMore();
109109
} else {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public class Binding {
2525
public boolean asExtraWhenPresent;
2626
public boolean isNullable = true;
2727
public boolean isCollectionValueNullable = true;
28-
public boolean shouldOmitNull = true;
28+
public OmitValue defaultValueToOmit;
2929
// then this property will not be unknown
3030
// but we do not want to bind it anywhere
3131
public boolean shouldSkip;

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

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,6 @@ 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-
}
113110
}
114111
return desc;
115112
}

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

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,16 @@ public class Config extends EmptyExtension {
1414
private static volatile Map<String, Config> configs = new HashMap<String, Config>();
1515
private volatile Map<Type, String> decoderCacheKeys = new HashMap<Type, String>();
1616
private volatile Map<Type, String> encoderCacheKeys = new HashMap<Type, String>();
17+
private final static Map<Class, OmitValue> primitiveOmitValues = new HashMap<Class, OmitValue>() {{
18+
put(boolean.class, new OmitValue.False());
19+
put(char.class, new OmitValue.ZeroChar());
20+
put(byte.class, new OmitValue.ZeroByte());
21+
put(short.class, new OmitValue.ZeroShort());
22+
put(int.class, new OmitValue.ZeroInt());
23+
put(long.class, new OmitValue.ZeroLong());
24+
put(float.class, new OmitValue.ZeroFloat());
25+
put(double.class, new OmitValue.ZeroDouble());
26+
}};
1727

1828
protected Config(String configName, Builder builder) {
1929
this.configName = configName;
@@ -76,6 +86,10 @@ public int indentionStep() {
7686
return builder.indentionStep;
7787
}
7888

89+
public boolean omitDefaultValue() {
90+
return builder.omitDefaultValue;
91+
}
92+
7993
public boolean escapeUnicode() {
8094
return builder.escapeUnicode;
8195
}
@@ -90,6 +104,7 @@ public static class Builder {
90104
private EncodingMode encodingMode;
91105
private int indentionStep;
92106
private boolean escapeUnicode = true;
107+
private boolean omitDefaultValue = false;
93108

94109
public Builder() {
95110
String envMode = System.getenv("JSONITER_DECODING_MODE");
@@ -121,6 +136,11 @@ public Builder indentionStep(int indentionStep) {
121136
return this;
122137
}
123138

139+
public Builder omitDefaultValue(boolean omitDefaultValue) {
140+
this.omitDefaultValue = omitDefaultValue;
141+
return this;
142+
}
143+
124144
public Builder escapeUnicode(boolean escapeUnicode) {
125145
this.escapeUnicode = escapeUnicode;
126146
return this;
@@ -159,6 +179,7 @@ public boolean equals(Object o) {
159179
if (indentionStep != builder.indentionStep) return false;
160180
if (escapeUnicode != builder.escapeUnicode) return false;
161181
if (decodingMode != builder.decodingMode) return false;
182+
if (omitDefaultValue != builder.omitDefaultValue) return false;
162183
return encodingMode == builder.encodingMode;
163184
}
164185

@@ -168,6 +189,7 @@ public int hashCode() {
168189
result = 31 * result + (encodingMode != null ? encodingMode.hashCode() : 0);
169190
result = 31 * result + indentionStep;
170191
result = 31 * result + (escapeUnicode ? 1 : 0);
192+
result = 31 * result + (omitDefaultValue ? 1 : 0);
171193
return result;
172194
}
173195

@@ -177,6 +199,7 @@ public Builder copy() {
177199
builder.decodingMode = decodingMode;
178200
builder.indentionStep = indentionStep;
179201
builder.escapeUnicode = escapeUnicode;
202+
builder.omitDefaultValue = omitDefaultValue;
180203
return builder;
181204
}
182205
}
@@ -349,6 +372,7 @@ private void detectCtor(ClassDescriptor desc) {
349372
}
350373

351374
private void updateBindings(ClassDescriptor desc) {
375+
boolean globalOmitDefault = JsoniterSpi.getCurrentConfig().omitDefaultValue();
352376
for (Binding binding : desc.allBindings()) {
353377
JsonIgnore jsonIgnore = getJsonIgnore(binding.annotations);
354378
if (jsonIgnore != null) {
@@ -365,6 +389,9 @@ private void updateBindings(ClassDescriptor desc) {
365389
binding.fromNames = new String[0];
366390
binding.toNames = new String[0];
367391
}
392+
if (globalOmitDefault) {
393+
binding.defaultValueToOmit = createOmitValue(binding.valueType);
394+
}
368395
JsonProperty jsonProperty = getJsonProperty(binding.annotations);
369396
if (jsonProperty != null) {
370397
updateBindingWithJsonProperty(binding, jsonProperty);
@@ -388,7 +415,10 @@ private void updateBindingWithJsonProperty(Binding binding, JsonProperty jsonPro
388415
binding.asMissingWhenNotPresent = jsonProperty.required();
389416
binding.isNullable = jsonProperty.nullable();
390417
binding.isCollectionValueNullable = jsonProperty.collectionValueNullable();
391-
binding.shouldOmitNull = jsonProperty.omitNull();
418+
String defaultValueToOmit = jsonProperty.defaultValueToOmit();
419+
if (!defaultValueToOmit.isEmpty()) {
420+
binding.defaultValueToOmit = OmitValue.Parsed.parse(binding.valueType, defaultValueToOmit);
421+
}
392422
String altName = jsonProperty.value();
393423
if (!altName.isEmpty()) {
394424
binding.name = altName;
@@ -419,6 +449,14 @@ private void updateBindingWithJsonProperty(Binding binding, JsonProperty jsonPro
419449
}
420450
}
421451

452+
protected OmitValue createOmitValue(Type valueType) {
453+
OmitValue omitValue = primitiveOmitValues.get(valueType);
454+
if (omitValue != null) {
455+
return omitValue;
456+
}
457+
return new OmitValue.Null();
458+
}
459+
422460
protected JsonWrapper getJsonWrapper(Annotation[] annotations) {
423461
return getAnnotation(annotations, JsonWrapper.class);
424462
}

0 commit comments

Comments
 (0)