Skip to content

Commit 303fcf8

Browse files
committed
customize field encoder
1 parent 14858c8 commit 303fcf8

13 files changed

Lines changed: 203 additions & 108 deletions

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ private static String genSource(String cacheKey, Class clazz, Type[] typeArgs) {
8787
ClassDescriptor desc = ExtensionManager.getClassDescriptor(clazz, false);
8888
List<Binding> allBindings = desc.allDecoderBindings();
8989
for (Binding allBinding : allBindings) {
90-
if (allBinding.isMandatory) {
90+
if (allBinding.failOnMissing) {
9191
// only slice support mandatory tracking
9292
return CodegenImplObject.genObjectUsingSlice(clazz, cacheKey, desc);
9393
}

src/main/java/com/jsoniter/CodegenImplNative.java

Lines changed: 0 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -84,70 +84,6 @@ public static String genReadOp(Type type) {
8484
}
8585
}
8686

87-
public static String genField(Binding field, String cacheKey) {
88-
String fieldCacheKey = field.name + "@" + cacheKey;
89-
if (field.decoder != null) {
90-
ExtensionManager.addNewDecoder(fieldCacheKey, field.decoder);
91-
}
92-
// the field decoder might be registered directly
93-
Decoder decoder = ExtensionManager.getDecoder(fieldCacheKey);
94-
Type fieldType = field.valueType;
95-
if (decoder == null) {
96-
return String.format("(%s)%s", getTypeName(fieldType), CodegenImplNative.genReadOp(fieldType));
97-
}
98-
if (fieldType == boolean.class) {
99-
if (!(decoder instanceof Decoder.BooleanDecoder)) {
100-
throw new JsonException("decoder for field " + field + "must implement Decoder.BooleanDecoder");
101-
}
102-
return String.format("com.jsoniter.CodegenAccess.readBoolean(\"%s\", iter)", fieldCacheKey);
103-
}
104-
if (fieldType == byte.class) {
105-
if (!(decoder instanceof Decoder.ShortDecoder)) {
106-
throw new JsonException("decoder for field " + field + "must implement Decoder.ShortDecoder");
107-
}
108-
return String.format("com.jsoniter.CodegenAccess.readShort(\"%s\", iter)", fieldCacheKey);
109-
}
110-
if (fieldType == short.class) {
111-
if (!(decoder instanceof Decoder.ShortDecoder)) {
112-
throw new JsonException("decoder for field " + field + "must implement Decoder.ShortDecoder");
113-
}
114-
return String.format("com.jsoniter.CodegenAccess.readShort(\"%s\", iter)", fieldCacheKey);
115-
}
116-
if (fieldType == char.class) {
117-
if (!(decoder instanceof Decoder.IntDecoder)) {
118-
throw new JsonException("decoder for field " + field + "must implement Decoder.IntDecoder");
119-
}
120-
return String.format("com.jsoniter.CodegenAccess.readInt(\"%s\", iter)", fieldCacheKey);
121-
}
122-
if (fieldType == int.class) {
123-
if (!(decoder instanceof Decoder.IntDecoder)) {
124-
throw new JsonException("decoder for field " + field + "must implement Decoder.IntDecoder");
125-
}
126-
return String.format("com.jsoniter.CodegenAccess.readInt(\"%s\", iter)", fieldCacheKey);
127-
}
128-
if (fieldType == long.class) {
129-
if (!(decoder instanceof Decoder.LongDecoder)) {
130-
throw new JsonException("decoder for field " + field + "must implement Decoder.LongDecoder");
131-
}
132-
return String.format("com.jsoniter.CodegenAccess.readLong(\"%s\", iter)", fieldCacheKey);
133-
}
134-
if (fieldType == float.class) {
135-
if (!(decoder instanceof Decoder.FloatDecoder)) {
136-
throw new JsonException("decoder for field " + field + "must implement Decoder.FloatDecoder");
137-
}
138-
return String.format("com.jsoniter.CodegenAccess.readFloat(\"%s\", iter)", fieldCacheKey);
139-
}
140-
if (fieldType == double.class) {
141-
if (!(decoder instanceof Decoder.DoubleDecoder)) {
142-
throw new JsonException("decoder for field " + field + "must implement Decoder.DoubleDecoder");
143-
}
144-
return String.format("com.jsoniter.CodegenAccess.readDouble(\"%s\", iter)", fieldCacheKey);
145-
}
146-
Codegen.getDecoder(fieldCacheKey, fieldType); // put decoder into cache
147-
return String.format("(%s)com.jsoniter.CodegenAccess.read(\"%s\", iter);",
148-
getTypeName(fieldType), fieldCacheKey);
149-
}
150-
15187
public static String getTypeName(Type fieldType) {
15288
if (fieldType instanceof Class) {
15389
Class clazz = (Class) fieldType;

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

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

3-
import com.jsoniter.spi.Binding;
4-
import com.jsoniter.spi.ClassDescriptor;
5-
import com.jsoniter.spi.ConstructorDescriptor;
6-
import com.jsoniter.spi.SetterDescriptor;
3+
import com.jsoniter.spi.*;
74

85
import java.lang.reflect.Type;
96
import java.util.*;
@@ -27,7 +24,7 @@ public static String genObjectUsingSlice(Class clazz, String cacheKey, ClassDesc
2724
List<Binding> allBindings = desc.allDecoderBindings();
2825
int currentIdx = 0;
2926
for (Binding binding : allBindings) {
30-
if (binding.isMandatory) {
27+
if (binding.failOnMissing) {
3128
binding.idx = currentIdx++;
3229
} else {
3330
binding.idx = -1;
@@ -184,8 +181,8 @@ private static void addFieldDispatch(
184181
append(lines, String.format("field.at(%d)==%s", i, b));
185182
append(lines, ") {");
186183
Binding field = (Binding) entry.getValue();
187-
append(lines, String.format("_%s_ = %s;", field.name, CodegenImplNative.genField(field, cacheKey)));
188-
if (field.isMandatory) {
184+
append(lines, String.format("_%s_ = %s;", field.name, genField(field, cacheKey)));
185+
if (field.failOnMissing) {
189186
long mask = 1L << field.idx;
190187
append(lines, "tracker = tracker | " + mask + "L;");
191188
}
@@ -307,9 +304,9 @@ private static void appendFieldSet(StringBuilder lines, String cacheKey, Constru
307304
if (!shouldReuseObject(field.valueType)) {
308305
append(lines, String.format("com.jsoniter.CodegenAccess.setExistingObject(iter, obj.%s);", field.name));
309306
}
310-
append(lines, String.format("obj.%s = %s;", field.name, CodegenImplNative.genField(field, cacheKey)));
307+
append(lines, String.format("obj.%s = %s;", field.name, genField(field, cacheKey)));
311308
} else {
312-
append(lines, String.format("_%s_ = %s;", field.name, CodegenImplNative.genField(field, cacheKey)));
309+
append(lines, String.format("_%s_ = %s;", field.name, genField(field, cacheKey)));
313310
}
314311
}
315312

@@ -388,4 +385,67 @@ public static boolean shouldReuseObject(Type valueType) {
388385
}
389386
return CodegenImplNative.isNative(valueType);
390387
}
388+
389+
public static String genField(Binding field, String cacheKey) {
390+
String fieldCacheKey = field.name + "@" + cacheKey;
391+
if (field.decoder != null) {
392+
ExtensionManager.addNewDecoder(fieldCacheKey, field.decoder);
393+
}
394+
// the field decoder might be registered directly
395+
Decoder decoder = ExtensionManager.getDecoder(fieldCacheKey);
396+
Type fieldType = field.valueType;
397+
if (decoder == null) {
398+
return String.format("(%s)%s", CodegenImplNative.getTypeName(fieldType), CodegenImplNative.genReadOp(fieldType));
399+
}
400+
if (fieldType == boolean.class) {
401+
if (!(decoder instanceof Decoder.BooleanDecoder)) {
402+
throw new JsonException("decoder for field " + field + "must implement Decoder.BooleanDecoder");
403+
}
404+
return String.format("com.jsoniter.CodegenAccess.readBoolean(\"%s\", iter)", fieldCacheKey);
405+
}
406+
if (fieldType == byte.class) {
407+
if (!(decoder instanceof Decoder.ShortDecoder)) {
408+
throw new JsonException("decoder for field " + field + "must implement Decoder.ShortDecoder");
409+
}
410+
return String.format("com.jsoniter.CodegenAccess.readShort(\"%s\", iter)", fieldCacheKey);
411+
}
412+
if (fieldType == short.class) {
413+
if (!(decoder instanceof Decoder.ShortDecoder)) {
414+
throw new JsonException("decoder for field " + field + "must implement Decoder.ShortDecoder");
415+
}
416+
return String.format("com.jsoniter.CodegenAccess.readShort(\"%s\", iter)", fieldCacheKey);
417+
}
418+
if (fieldType == char.class) {
419+
if (!(decoder instanceof Decoder.IntDecoder)) {
420+
throw new JsonException("decoder for field " + field + "must implement Decoder.IntDecoder");
421+
}
422+
return String.format("com.jsoniter.CodegenAccess.readInt(\"%s\", iter)", fieldCacheKey);
423+
}
424+
if (fieldType == int.class) {
425+
if (!(decoder instanceof Decoder.IntDecoder)) {
426+
throw new JsonException("decoder for field " + field + "must implement Decoder.IntDecoder");
427+
}
428+
return String.format("com.jsoniter.CodegenAccess.readInt(\"%s\", iter)", fieldCacheKey);
429+
}
430+
if (fieldType == long.class) {
431+
if (!(decoder instanceof Decoder.LongDecoder)) {
432+
throw new JsonException("decoder for field " + field + "must implement Decoder.LongDecoder");
433+
}
434+
return String.format("com.jsoniter.CodegenAccess.readLong(\"%s\", iter)", fieldCacheKey);
435+
}
436+
if (fieldType == float.class) {
437+
if (!(decoder instanceof Decoder.FloatDecoder)) {
438+
throw new JsonException("decoder for field " + field + "must implement Decoder.FloatDecoder");
439+
}
440+
return String.format("com.jsoniter.CodegenAccess.readFloat(\"%s\", iter)", fieldCacheKey);
441+
}
442+
if (fieldType == double.class) {
443+
if (!(decoder instanceof Decoder.DoubleDecoder)) {
444+
throw new JsonException("decoder for field " + field + "must implement Decoder.DoubleDecoder");
445+
}
446+
return String.format("com.jsoniter.CodegenAccess.readDouble(\"%s\", iter)", fieldCacheKey);
447+
}
448+
return String.format("(%s)com.jsoniter.CodegenAccess.read(\"%s\", iter);",
449+
CodegenImplNative.getTypeName(fieldType), fieldCacheKey);
450+
}
391451
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,6 @@ private static String genSource(String cacheKey, Class clazz, Type[] typeArgs) {
8181
if (Collection.class.isAssignableFrom(clazz)) {
8282
return CodegenImplArray.genCollection(clazz, typeArgs);
8383
}
84-
return CodegenImplObject.genObject(clazz);
84+
return CodegenImplObject.genObject(cacheKey, clazz);
8585
}
8686
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.jsoniter.output;
2+
3+
import com.jsoniter.spi.ExtensionManager;
4+
5+
import java.io.IOException;
6+
7+
public class CodegenAccess {
8+
public static void writeVal(String cacheKey, Object obj, JsonStream stream) throws IOException {
9+
ExtensionManager.getEncoder(cacheKey).encode(obj, stream);
10+
}
11+
}

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

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
package com.jsoniter.output;
22

3-
import com.jsoniter.spi.Binding;
4-
import com.jsoniter.spi.ClassDescriptor;
5-
import com.jsoniter.spi.ExtensionManager;
3+
import com.jsoniter.*;
4+
import com.jsoniter.spi.*;
65

76
class CodegenImplObject {
8-
public static String genObject(Class clazz) {
7+
public static String genObject(String cacheKey, Class clazz) {
98
ClassDescriptor desc = ExtensionManager.getClassDescriptor(clazz, false);
109
StringBuilder lines = new StringBuilder();
1110
append(lines, "public static void encode_(Object rawObj, com.jsoniter.output.JsonStream stream) {");
@@ -16,9 +15,9 @@ public static String genObject(Class clazz) {
1615
append(lines, "{{clazz}} obj = ({{clazz}})rawObj;");
1716
append(lines, "stream.startObject();");
1817
for (Binding field : desc.allEncoderBindings()) {
19-
for (String fromName : field.fromNames) {
20-
append(lines, String.format("stream.writeField(\"%s\");", field.name));
21-
append(lines, CodegenImplNative.genWriteOp("obj." + fromName, field.valueType));
18+
for (String toName : field.toNames) {
19+
append(lines, String.format("stream.writeField(\"%s\");", toName));
20+
append(lines, genField(cacheKey, field));
2221
append(lines, "stream.writeMore();");
2322
}
2423
}
@@ -28,6 +27,20 @@ public static String genObject(Class clazz) {
2827
return lines.toString().replace("{{clazz}}", clazz.getCanonicalName());
2928
}
3029

30+
private static String genField(String cacheKey, Binding field) {
31+
String fieldCacheKey = field.name + "@" + cacheKey;
32+
if (field.encoder != null) {
33+
ExtensionManager.addNewEncoder(fieldCacheKey, field.encoder);
34+
}
35+
// the field decoder might be registered directly
36+
Encoder encoder = ExtensionManager.getEncoder(fieldCacheKey);
37+
if (encoder == null) {
38+
return CodegenImplNative.genWriteOp("obj." + field.name, field.valueType);
39+
}
40+
return String.format("com.jsoniter.output.CodegenAccess.writeVal(\"%s\", obj.%s, stream);",
41+
fieldCacheKey, field.name);
42+
}
43+
3144
private static void append(StringBuilder lines, String str) {
3245
lines.append(str);
3346
lines.append("\n");

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public final void writeVal(String val) throws IOException {
7878

7979
public final void writeRaw(String val) throws IOException {
8080
// TODO: do not allocate new buffer every time, encode utf16 into utf8 directly
81-
out.write(val.getBytes(charset));
81+
write(val.getBytes(charset));
8282
}
8383

8484
public final void writeVal(Boolean val) throws IOException {

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@ public class Binding {
1212
public TypeLiteral valueTypeLiteral;
1313
public Annotation[] annotations;
1414
// output
15-
public String[] fromNames;
15+
public String[] fromNames; // for decoder
16+
public String[] toNames; // for encoder
1617
public Decoder decoder;
17-
public boolean isMandatory;
18+
public Encoder encoder;
19+
public boolean failOnMissing;
1820
// optional
1921
public Field field;
2022
public int idx;

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,18 @@ public static void registerTypeEncoder(Class clazz, Encoder encoder) {
4040
addNewEncoder(TypeLiteral.generateEncoderCacheKey(clazz), encoder);
4141
}
4242

43+
public static void registerTypeEncoder(TypeLiteral typeLiteral, Encoder encoder) {
44+
addNewEncoder(typeLiteral.getCacheKey(), encoder);
45+
}
46+
47+
public static void registerFieldEncoder(Class clazz, String field, Encoder encoder) {
48+
addNewEncoder(field + "@" + TypeLiteral.generateEncoderCacheKey(clazz), encoder);
49+
}
50+
51+
public static void registerFieldEncoder(TypeLiteral typeLiteral, String field, Encoder encoder) {
52+
addNewEncoder(field + "@" + typeLiteral.getCacheKey(), encoder);
53+
}
54+
4355
public static Decoder getDecoder(String cacheKey) {
4456
return decoders.get(cacheKey);
4557
}
@@ -77,11 +89,20 @@ public static ClassDescriptor getClassDescriptor(Class clazz, boolean includingP
7789
if (desc.ctor.staticFactory != null) {
7890
desc.ctor.staticFactory.setAccessible(true);
7991
}
92+
for (SetterDescriptor setter : desc.setters) {
93+
setter.method.setAccessible(true);
94+
}
8095
}
8196
for (Binding binding : desc.allDecoderBindings()) {
8297
if (binding.fromNames == null) {
8398
binding.fromNames = new String[]{binding.name};
8499
}
100+
if (binding.toNames == null) {
101+
binding.toNames = new String[]{binding.name};
102+
}
103+
if (binding.field != null && includingPrivate) {
104+
binding.field.setAccessible(true);
105+
}
85106
binding.clazz = clazz;
86107
binding.valueTypeLiteral = createTypeLiteral(binding.valueType);
87108
}

src/test/java/com/jsoniter/TestCustomizeField.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ public void updateClassDescriptor(ClassDescriptor desc) {
180180
return;
181181
}
182182
for (Binding field : desc.allDecoderBindings()) {
183-
field.isMandatory = true;
183+
field.failOnMissing = true;
184184
}
185185
}
186186
});
@@ -202,7 +202,7 @@ public void updateClassDescriptor(ClassDescriptor desc) {
202202
return;
203203
}
204204
for (Binding field : desc.allDecoderBindings()) {
205-
field.isMandatory = true;
205+
field.failOnMissing = true;
206206
}
207207
}
208208
});

0 commit comments

Comments
 (0)