Skip to content

Commit 590015e

Browse files
authored
Update java-extension.md
1 parent 75224ea commit 590015e

File tree

1 file changed

+54
-66
lines changed

1 file changed

+54
-66
lines changed

java-extension.md

Lines changed: 54 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -114,22 +114,25 @@ public class TestObject4 {
114114
public int field1;
115115
}
116116

117-
JsonIterator.registerExtension(new EmptyExtension() {
117+
ExtensionManager.registerExtension(new EmptyExtension() {
118118
@Override
119-
public boolean updateBinding(Binding field) {
120-
if (field.clazz == TestObject4.class && field.name.equals("field1")) {
121-
field.fromNames = new String[]{"field_1", "Field1"};
122-
return true;
119+
public void updateClassDescriptor(ClassDescriptor desc) {
120+
if (desc.clazz != TestObject4.class) {
121+
return;
122+
}
123+
for (Binding field : desc.allDecoderBindings()) {
124+
if (field.name.equals("field1")) {
125+
field.fromNames = new String[]{"field_1", "Field1"};
126+
}
123127
}
124-
return false;
125128
}
126129
});
127130
JsonIterator iter = JsonIterator.parse("{'field_1': 100}".replace('\'', '"'));
128131
TestObject4 myObject1 = iter.read(TestObject4.class);
129132
assertEquals(100, myObject1.field1);
130133
```
131134

132-
The callback `updateBinding` change the data source or decoder for a field. The `fromNames` is a `String[]`, with following options:
135+
The callback `updateClassDescriptor` change the data source or decoder for a field. The `fromNames` is a `String[]`, with following options:
133136

134137
* null: do not customize this field
135138
* empty array: disable this field binding. works like `@JsonIgnore`
@@ -143,18 +146,26 @@ public class Binding {
143146
public Class clazz;
144147
public String name;
145148
public Type valueType;
149+
public TypeLiteral valueTypeLiteral;
146150
public Annotation[] annotations;
147151
// output
148-
public String[] fromNames;
152+
public String[] fromNames; // for decoder
153+
public String[] toNames; // for encoder
149154
public Decoder decoder;
155+
public Encoder encoder;
156+
public boolean failOnMissing;
157+
public boolean failOnPresent;
158+
// optional
159+
public Field field;
160+
public int idx;
150161
}
151162
```
152163

153164
So we can also customize the field decoder using `Extension` to update bindings. Actually type decoder and field decoder callback is just a shortcut. The `Extension` interface can do all the job.
154165

155166
# Your own annotation support
156167

157-
It is very common to support `@JsonProperty` to rename field. By registering your own extension, you can implement them very easily.
168+
It is very common to support `@JsonProperty` to rename field. Jsoniter also comes with built-in support for annotation. But by registering your own extension, you can implement them very easily.
158169

159170
```java
160171
@Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@@ -166,21 +177,21 @@ public @interface JsonProperty {
166177

167178
public class MyAnnotationSupport extends EmptyExtension {
168179

169-
public static void enable() {
170-
JsonIterator.registerExtension(new JsoniterAnnotationSupport());
171-
}
172-
173180
@Override
174-
public boolean updateBinding(Binding field) {
175-
JsonProperty jsonProperty = field.getAnnotation(JsonProperty.class);
176-
if (jsonProperty != null) {
177-
String alternativeField = jsonProperty.value();
178-
if (!alternativeField.isEmpty()) {
179-
field.fromNames = new String[]{alternativeField};
180-
return true;
181+
public void updateClassDescriptor(ClassDescriptor desc) {
182+
for (Binding field : desc.allDecoderBindings()) {
183+
JsonIgnore jsonIgnore = field.getAnnotation(JsonIgnore.class);
184+
if (jsonIgnore != null) {
185+
field.fromNames = new String[0];
186+
}
187+
JsonProperty jsonProperty = field.getAnnotation(JsonProperty.class);
188+
if (jsonProperty != null) {
189+
String alternativeField = jsonProperty.value();
190+
if (!alternativeField.isEmpty()) {
191+
field.fromNames = new String[]{alternativeField};
192+
}
181193
}
182194
}
183-
return false;
184195
}
185196
}
186197
```
@@ -205,25 +216,21 @@ If you do not want to write annotation support yourself, you can use built-in `c
205216
Jsoniter can work with class without default constructor. The extension can customize the constructor to be used for specific class. The constructor can also be a static method, instead of real constructor.
206217

207218
```java
208-
public interface Extension {
209-
// ...
210-
CustomizedConstructor getConstructor(Class clazz);
211-
/// ...
212-
}
213-
214-
public class CustomizedConstructor {
219+
public class ConstructorDescriptor {
215220
/**
216221
* set to null if use constructor
217222
* otherwise use static method
218223
*/
219224
public String staticMethodName;
225+
// optional
226+
public Constructor ctor;
227+
// optional
228+
public Method staticFactory;
220229

221230
/**
222231
* the parameters to call constructor or static method
223232
*/
224233
public List<Binding> parameters = new ArrayList<Binding>();
225-
226-
public static CustomizedConstructor DEFAULT_INSTANCE = new CustomizedConstructor();
227234
}
228235
```
229236

@@ -290,13 +297,7 @@ public static class WithSetter {
290297
This annotation support is implemented by `Extension` as well. The callback is:
291298

292299
```java
293-
public interface Extension {
294-
// ...
295-
List<CustomizedSetter> getSetters(Class clazz);
296-
// ...
297-
}
298-
299-
public class CustomizedSetter {
300+
public class SetterDescriptor {
300301
/**
301302
* which method to call to set value
302303
*/
@@ -306,6 +307,9 @@ public class CustomizedSetter {
306307
* the parameters to bind
307308
*/
308309
public List<Binding> parameters = new ArrayList<Binding>();
310+
311+
// optional
312+
public Method method;
309313
}
310314
```
311315

@@ -318,35 +322,18 @@ public interface Extension {
318322
/**
319323
* Customize type decoding
320324
*
325+
* @param cacheKey
321326
* @param type change how to decode the type
322-
* @param typeArgs for generic type, there might be arguments
323327
* @return null, if no special customization needed
324328
*/
325-
Decoder createDecoder(Type type, Type... typeArgs);
326-
327-
/**
328-
* Customize the binding source or decoder
329-
*
330-
* @param field binding information
331-
* @return true, if stops other extension from customizing same field
332-
*/
333-
boolean updateBinding(Binding field);
334-
335-
/**
336-
* Customize which constructor to call
337-
*
338-
* @param clazz the class of instance to create
339-
* @return null, if fallback to default behavior
340-
*/
341-
CustomizedConstructor getConstructor(Class clazz);
329+
Decoder createDecoder(String cacheKey, Type type);
342330

343331
/**
344-
* Customize setters to call after instance is created and fields set
332+
* Update binding is done for the class
345333
*
346-
* @param clazz the class that is binding
347-
* @return null, if fallback to default behavior
334+
* @param desc binding information
348335
*/
349-
List<CustomizedSetter> getSetters(Class clazz);
336+
void updateClassDescriptor(ClassDescriptor desc);
350337
}
351338
```
352339

@@ -362,7 +349,7 @@ Also the decoder interface is powered with iterator-api to iterate on the lowest
362349
There is a feature flag of jackson called `ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT`. The intention is to decode input like `[]` as null, as PHP might treat empty object as empty array. To support this feature, we can register a extension
363350

364351
```java
365-
JsonIterator.registerExtension(new EmptyExtension() {
352+
ExtensionManager.registerExtension(new EmptyExtension() {
366353
@Override
367354
public Decoder createDecoder(final String cacheKey, final Type type) {
368355
if (cacheKey.endsWith(".original")) {
@@ -374,18 +361,19 @@ JsonIterator.registerExtension(new EmptyExtension() {
374361
}
375362
return new Decoder() {
376363
@Override
377-
public Object decode(JsonIterator iter) throws IOException {
378-
if (iter.whatIsNext() == ValueType.ARRAY) {
379-
if (iter.readArray()) {
364+
public Object decode(JsonIterator iter1) throws IOException {
365+
if (iter1.whatIsNext() == ValueType.ARRAY) {
366+
if (iter1.readArray()) {
380367
// none empty array
381-
throw iter.reportError("decode [] as null", "only empty array is expected");
368+
throw iter1.reportError("decode [] as null", "only empty array is expected");
382369
} else {
383370
return null;
384371
}
385372
} else {
386373
// just use original decoder
387-
TypeLiteral typeLiteral = TypeLiteral.create(type, "original");
388-
return iter.read(typeLiteral);
374+
TypeLiteral typeLiteral = new TypeLiteral(type, cacheKey + ".original",
375+
TypeLiteral.generateEncoderCacheKey(type));
376+
return iter1.read(typeLiteral);
389377
}
390378
}
391379
};

0 commit comments

Comments
 (0)