Skip to content

Commit cb9b607

Browse files
committed
#77 implement defaultValueToOmit
1 parent 7c1bdb9 commit cb9b607

File tree

14 files changed

+398
-127
lines changed

14 files changed

+398
-127
lines changed

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: 22 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -82,59 +82,35 @@ 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().omitZero();
85+
boolean omitZero = JsoniterSpi.getCurrentConfig().omitDefaultValue();
8686
if (!binding.isNullable) {
8787
nullable = false;
8888
}
89-
if (nullable) {
90-
if (binding.shouldOmitNull) {
91-
if (notFirst == 0) { // no previous field
92-
notFirst = 2; // maybe
93-
ctx.append("boolean notFirst = false;");
94-
}
95-
ctx.append(String.format("if (%s != null) {", valueAccessor));
96-
notFirst = appendComma(ctx, notFirst);
97-
if (noIndention) {
98-
ctx.append(CodegenResult.bufferToWriteOp("\"" + toName + "\":"));
99-
} else {
100-
ctx.append(String.format("stream.writeObjectField(\"%s\");", toName));
101-
}
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 + "\":"));
10299
} else {
103-
notFirst = appendComma(ctx, notFirst);
104-
if (noIndention) {
105-
ctx.buffer('"');
106-
ctx.buffer(toName);
107-
ctx.buffer('"');
108-
ctx.buffer(':');
109-
} else {
110-
ctx.append(String.format("stream.writeObjectField(\"%s\");", toName));
111-
}
112-
ctx.append(String.format("if (%s == null) { stream.writeNull(); } else {", valueAccessor));
100+
ctx.append(String.format("stream.writeObjectField(\"%s\");", toName));
113101
}
114102
} else {
115-
if (encoder == null && valueClazz.isPrimitive() && !(valueClazz == String.class) && omitZero) {
116-
if (notFirst == 0) { // no previous field
117-
notFirst = 2; // maybe
118-
ctx.append("boolean notFirst = false;");
119-
}
120-
String t = CodegenImplNative.getTypeName(binding.valueType);
121-
ctx.append(String.format("if (!(((%s)%s) == 0)) {", t, valueAccessor));
122-
notFirst = appendComma(ctx, notFirst);
123-
if (noIndention) {
124-
ctx.append(CodegenResult.bufferToWriteOp("\"" + toName + "\":"));
125-
} else {
126-
ctx.append(String.format("stream.writeObjectField(\"%s\");", toName));
127-
}
103+
notFirst = appendComma(ctx, notFirst);
104+
if (noIndention) {
105+
ctx.buffer('"');
106+
ctx.buffer(toName);
107+
ctx.buffer('"');
108+
ctx.buffer(':');
128109
} else {
129-
notFirst = appendComma(ctx, notFirst);
130-
if (noIndention) {
131-
ctx.buffer('"');
132-
ctx.buffer(toName);
133-
ctx.buffer('"');
134-
ctx.buffer(':');
135-
} else {
136-
ctx.append(String.format("stream.writeObjectField(\"%s\");", toName));
137-
}
110+
ctx.append(String.format("stream.writeObjectField(\"%s\");", toName));
111+
}
112+
if (nullable) {
113+
ctx.append(String.format("if (%s == null) { stream.writeNull(); } else {", valueAccessor));
138114
}
139115
}
140116
if (encoder == null) {

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

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -499,12 +499,6 @@ public static void setIndentionStep(int indentionStep) {
499499
JsoniterSpi.setCurrentConfig(newConfig);
500500
}
501501

502-
public static void setOmitZero(boolean omitZero) {
503-
Config newConfig = JsoniterSpi.getDefaultConfig().copyBuilder().omitZero(omitZero).build();
504-
JsoniterSpi.setDefaultConfig(newConfig);
505-
JsoniterSpi.setCurrentConfig(newConfig);
506-
}
507-
508502
public static void registerNativeEncoder(Class clazz, Encoder.ReflectionEncoder encoder) {
509503
CodegenImplNative.NATIVE_ENCODERS.put(clazz, encoder);
510504
}

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

Lines changed: 2 additions & 5 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,10 +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)) {
107-
if (JsoniterSpi.getCurrentConfig().omitZero() && val instanceof Number && ((Number) val).doubleValue() == 0) {
108-
return notFirst;
109-
}
105+
OmitValue defaultValueToOmit = encodeTo.binding.defaultValueToOmit;
106+
if (!(defaultValueToOmit != null && defaultValueToOmit.shouldOmit(val))) {
110107
if (notFirst) {
111108
stream.writeMore();
112109
} 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: 34 additions & 9 deletions
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,8 +86,8 @@ public int indentionStep() {
7686
return builder.indentionStep;
7787
}
7888

79-
public boolean omitZero() {
80-
return builder.omitZero;
89+
public boolean omitDefaultValue() {
90+
return builder.omitDefaultValue;
8191
}
8292

8393
public boolean escapeUnicode() {
@@ -94,7 +104,7 @@ public static class Builder {
94104
private EncodingMode encodingMode;
95105
private int indentionStep;
96106
private boolean escapeUnicode = true;
97-
private boolean omitZero;
107+
private boolean omitDefaultValue = false;
98108

99109
public Builder() {
100110
String envMode = System.getenv("JSONITER_DECODING_MODE");
@@ -126,8 +136,8 @@ public Builder indentionStep(int indentionStep) {
126136
return this;
127137
}
128138

129-
public Builder omitZero(boolean b) {
130-
omitZero = b;
139+
public Builder omitDefaultValue(boolean omitDefaultValue) {
140+
this.omitDefaultValue = omitDefaultValue;
131141
return this;
132142
}
133143

@@ -169,7 +179,7 @@ public boolean equals(Object o) {
169179
if (indentionStep != builder.indentionStep) return false;
170180
if (escapeUnicode != builder.escapeUnicode) return false;
171181
if (decodingMode != builder.decodingMode) return false;
172-
if (omitZero != builder.omitZero) return false;
182+
if (omitDefaultValue != builder.omitDefaultValue) return false;
173183
return encodingMode == builder.encodingMode;
174184
}
175185

@@ -179,7 +189,7 @@ public int hashCode() {
179189
result = 31 * result + (encodingMode != null ? encodingMode.hashCode() : 0);
180190
result = 31 * result + indentionStep;
181191
result = 31 * result + (escapeUnicode ? 1 : 0);
182-
result = 31 * result + (omitZero ? 1 : 0);
192+
result = 31 * result + (omitDefaultValue ? 1 : 0);
183193
return result;
184194
}
185195

@@ -189,7 +199,7 @@ public Builder copy() {
189199
builder.decodingMode = decodingMode;
190200
builder.indentionStep = indentionStep;
191201
builder.escapeUnicode = escapeUnicode;
192-
builder.omitZero = omitZero;
202+
builder.omitDefaultValue = omitDefaultValue;
193203
return builder;
194204
}
195205
}
@@ -362,6 +372,7 @@ private void detectCtor(ClassDescriptor desc) {
362372
}
363373

364374
private void updateBindings(ClassDescriptor desc) {
375+
boolean globalOmitDefault = JsoniterSpi.getCurrentConfig().omitDefaultValue();
365376
for (Binding binding : desc.allBindings()) {
366377
JsonIgnore jsonIgnore = getJsonIgnore(binding.annotations);
367378
if (jsonIgnore != null) {
@@ -378,6 +389,9 @@ private void updateBindings(ClassDescriptor desc) {
378389
binding.fromNames = new String[0];
379390
binding.toNames = new String[0];
380391
}
392+
if (globalOmitDefault) {
393+
binding.defaultValueToOmit = createOmitValue(binding.valueType);
394+
}
381395
JsonProperty jsonProperty = getJsonProperty(binding.annotations);
382396
if (jsonProperty != null) {
383397
updateBindingWithJsonProperty(binding, jsonProperty);
@@ -401,7 +415,10 @@ private void updateBindingWithJsonProperty(Binding binding, JsonProperty jsonPro
401415
binding.asMissingWhenNotPresent = jsonProperty.required();
402416
binding.isNullable = jsonProperty.nullable();
403417
binding.isCollectionValueNullable = jsonProperty.collectionValueNullable();
404-
binding.shouldOmitNull = jsonProperty.omitNull();
418+
String defaultValueToOmit = jsonProperty.defaultValueToOmit();
419+
if (!defaultValueToOmit.isEmpty()) {
420+
binding.defaultValueToOmit = OmitValue.Parsed.parse(binding.valueType, defaultValueToOmit);
421+
}
405422
String altName = jsonProperty.value();
406423
if (!altName.isEmpty()) {
407424
binding.name = altName;
@@ -432,6 +449,14 @@ private void updateBindingWithJsonProperty(Binding binding, JsonProperty jsonPro
432449
}
433450
}
434451

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+
435460
protected JsonWrapper getJsonWrapper(Annotation[] annotations) {
436461
return getAnnotation(annotations, JsonWrapper.class);
437462
}

0 commit comments

Comments
 (0)