Skip to content

Commit 7bd9298

Browse files
committed
support customize field names
1 parent 8702833 commit 7bd9298

File tree

6 files changed

+128
-14
lines changed

6 files changed

+128
-14
lines changed

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

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ class Codegen {
3636
add(Vector.class);
3737
}};
3838
static volatile Map<String, Decoder> cache = new HashMap<String, Decoder>();
39+
static List<FieldDecoderFactory> fieldDecoderFactories = new ArrayList<FieldDecoderFactory>();
3940
static ClassPool pool = ClassPool.getDefault();
4041

4142
static Decoder getDecoder(String cacheKey, Type type, Type... typeArgs) {
@@ -160,22 +161,32 @@ public static void addNewDecoder(String cacheKey, Decoder decoder) {
160161
private static String genObject(Class clazz, String cacheKey) {
161162
Map<Integer, Object> map = new HashMap<Integer, Object>();
162163
for (Field field : clazz.getFields()) {
163-
byte[] fieldName = field.getName().getBytes();
164-
Map<Byte, Object> current = (Map<Byte, Object>) map.get(fieldName.length);
165-
if (current == null) {
166-
current = new HashMap<Byte, Object>();
167-
map.put(fieldName.length, current);
164+
Decoder decoder = createFieldDecoder(cacheKey, field);
165+
String[] alternativeFieldNames = null;
166+
if (decoder instanceof FieldDecoder) {
167+
alternativeFieldNames = ((FieldDecoder) decoder).getAlternativeFieldNames();
168168
}
169-
for (int i = 0; i < fieldName.length - 1; i++) {
170-
byte b = fieldName[i];
171-
Map<Byte, Object> next = (Map<Byte, Object>) current.get(b);
172-
if (next == null) {
173-
next = new HashMap<Byte, Object>();
174-
current.put(b, next);
169+
if (alternativeFieldNames == null) {
170+
alternativeFieldNames = new String[]{field.getName()};
171+
}
172+
for (String alternativeFieldName : alternativeFieldNames) {
173+
byte[] fieldName = alternativeFieldName.getBytes();
174+
Map<Byte, Object> current = (Map<Byte, Object>) map.get(fieldName.length);
175+
if (current == null) {
176+
current = new HashMap<Byte, Object>();
177+
map.put(fieldName.length, current);
178+
}
179+
for (int i = 0; i < fieldName.length - 1; i++) {
180+
byte b = fieldName[i];
181+
Map<Byte, Object> next = (Map<Byte, Object>) current.get(b);
182+
if (next == null) {
183+
next = new HashMap<Byte, Object>();
184+
current.put(b, next);
185+
}
186+
current = next;
175187
}
176-
current = next;
188+
current.put(fieldName[fieldName.length - 1], field);
177189
}
178-
current.put(fieldName[fieldName.length - 1], field);
179190
}
180191
if (map.isEmpty()) {
181192
StringBuilder lines = new StringBuilder();
@@ -206,6 +217,20 @@ private static String genObject(Class clazz, String cacheKey) {
206217
return lines.toString().replace("{{clazz}}", clazz.getName());
207218
}
208219

220+
private static Decoder createFieldDecoder(String cacheKey, Field field) {
221+
String fieldCacheKey = field.getName() + "@" + cacheKey;
222+
for (FieldDecoderFactory fieldDecoderFactory : fieldDecoderFactories) {
223+
Decoder decoder = fieldDecoderFactory.createDecoder(field);
224+
if (decoder != null) {
225+
addNewDecoder(fieldCacheKey, decoder);
226+
break;
227+
}
228+
}
229+
// the decoder can be just created by the factory
230+
// or it can be registered directly
231+
return cache.get(fieldCacheKey);
232+
}
233+
209234
private static void addFieldDispatch(StringBuilder lines, int len, int i, Map<Byte, Object> current, String cacheKey) {
210235
for (Map.Entry<Byte, Object> entry : current.entrySet()) {
211236
Byte b = entry.getKey();
@@ -221,8 +246,8 @@ private static void addFieldDispatch(StringBuilder lines, int len, int i, Map<By
221246
}
222247

223248
private static void genField(StringBuilder lines, Field field, String cacheKey) {
224-
String fieldCacheKey = field.getName() + "@" + cacheKey;
225249
String fieldTypeName = field.getType().getCanonicalName();
250+
String fieldCacheKey = field.getName() + "@" + cacheKey;
226251
if (cache.containsKey(fieldCacheKey)) {
227252
append(lines, String.format("obj.%s = (%s)iter.read(\"%s\", %s.class);",
228253
field.getName(), fieldTypeName, fieldCacheKey, fieldTypeName));
@@ -399,4 +424,8 @@ private static void append(StringBuilder lines, String str) {
399424
lines.append(str);
400425
lines.append("\n");
401426
}
427+
428+
public static void addFieldDecoderFactory(FieldDecoderFactory fieldDecoderFactory) {
429+
fieldDecoderFactories.add(fieldDecoderFactory);
430+
}
402431
}

src/main/java/com/jsoniter/Decoder.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,13 @@
44
import java.lang.reflect.Type;
55

66
public interface Decoder {
7+
/**
8+
* Customized decoder to read values from iterator
9+
*
10+
* @param type the type of object we are setting value to
11+
* @param iter the iterator instance
12+
* @return the value to set
13+
* @throws IOException
14+
*/
715
Object decode(Type type, Jsoniter iter) throws IOException;
816
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.jsoniter;
2+
3+
public interface FieldDecoder extends Decoder {
4+
/**
5+
* if json field does not match your object, specify what they can be
6+
*
7+
* @return null, if field is the field name
8+
*/
9+
public String[] getAlternativeFieldNames();
10+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.jsoniter;
2+
3+
import java.lang.reflect.Field;
4+
5+
public interface FieldDecoderFactory {
6+
/**
7+
* Customize field of certain kind, for example having certain annotation
8+
*
9+
* @param field the field reflection object
10+
* @return null, if no special customization needed
11+
*/
12+
Decoder createDecoder(Field field);
13+
}

src/main/java/com/jsoniter/Jsoniter.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -995,4 +995,8 @@ public static void registerFieldDecoder(TypeLiteral typeLiteral, String field, D
995995
public static void clearDecoders() {
996996
Codegen.cache.clear();
997997
}
998+
999+
public static void registerFieldDecoderFactory(FieldDecoderFactory fieldDecoderFactory) {
1000+
Codegen.addFieldDecoderFactory(fieldDecoderFactory);
1001+
}
9981002
}

test/main/java/com/jsoniter/TestCustomize.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import junit.framework.TestCase;
44

55
import java.io.IOException;
6+
import java.lang.reflect.Field;
67
import java.lang.reflect.Type;
78
import java.util.Date;
89

@@ -31,4 +32,53 @@ public Object decode(Type type, Jsoniter iter) throws IOException {
3132
CustomizedObject myObject = iter.read(CustomizedObject.class);
3233
assertEquals("100", myObject.field1);
3334
}
35+
36+
public void test_customize_all_fields() throws IOException {
37+
Jsoniter.registerFieldDecoderFactory(new FieldDecoderFactory() {
38+
@Override
39+
public Decoder createDecoder(Field field) {
40+
if (field.getDeclaringClass() == CustomizedObject.class && field.getName().equals("field1")) {
41+
return new Decoder(){
42+
43+
@Override
44+
public Object decode(Type type, Jsoniter iter) throws IOException {
45+
return Integer.toString(iter.readInt());
46+
}
47+
};
48+
}
49+
return null;
50+
}
51+
});
52+
Jsoniter iter = Jsoniter.parse("{'field1': 100}".replace('\'', '"'));
53+
CustomizedObject myObject = iter.read(CustomizedObject.class);
54+
assertEquals("100", myObject.field1);
55+
}
56+
57+
public void test_change_field_name() throws IOException {
58+
Jsoniter.registerFieldDecoderFactory(new FieldDecoderFactory() {
59+
@Override
60+
public Decoder createDecoder(Field field) {
61+
if (field.getDeclaringClass() == CustomizedObject.class && field.getName().equals("field1")) {
62+
return new FieldDecoder(){
63+
64+
@Override
65+
public String[] getAlternativeFieldNames() {
66+
return new String[]{"field_1", "Field1"};
67+
}
68+
69+
@Override
70+
public Object decode(Type type, Jsoniter iter) throws IOException {
71+
return Integer.toString(iter.readInt());
72+
}
73+
};
74+
}
75+
return null;
76+
}
77+
});
78+
Jsoniter iter = Jsoniter.parse("{'field_1': 100}{'Field1': 101}".replace('\'', '"'));
79+
CustomizedObject myObject1 = iter.read(CustomizedObject.class);
80+
assertEquals("100", myObject1.field1);
81+
CustomizedObject myObject2 = iter.read(CustomizedObject.class);
82+
assertEquals("101", myObject2.field1);
83+
}
3484
}

0 commit comments

Comments
 (0)