Skip to content

Commit 8d62f2d

Browse files
committed
extension can be object factory
1 parent d312340 commit 8d62f2d

9 files changed

Lines changed: 131 additions & 24 deletions

File tree

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

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -446,30 +446,26 @@ private static void appendVarDef(StringBuilder lines, Binding parameter) {
446446
append(lines, String.format("%s _%s_ = %s;", typeName, parameter.name, DEFAULT_VALUES.get(typeName)));
447447
}
448448

449-
public static String genObjectUsingSkip(Class clazz, ConstructorDescriptor ctor) {
450-
StringBuilder lines = new StringBuilder();
451-
append(lines, "if (iter.readNull()) { return null; }");
452-
append(lines, "{{clazz}} set = {{newInst}};");
453-
append(lines, "iter.skip();");
454-
append(lines, "return set;");
455-
return lines.toString()
456-
.replace("{{clazz}}", clazz.getCanonicalName())
457-
.replace("{{newInst}}", genNewInstCode(clazz, ctor));
458-
}
459-
460449
private static String genNewInstCode(Class clazz, ConstructorDescriptor ctor) {
461450
StringBuilder code = new StringBuilder();
462451
if (ctor.parameters.isEmpty()) {
463452
// nothing to bind, safe to reuse existing set
464453
code.append("(com.jsoniter.CodegenAccess.existingObject(iter) == null ? ");
465454
}
466-
if (ctor.staticMethodName == null) {
467-
code.append(String.format("new %s", clazz.getCanonicalName()));
455+
if (ctor.objectFactory != null) {
456+
code.append(String.format("(%s)com.jsoniter.spi.JsoniterSpi.create(%s.class)",
457+
clazz.getCanonicalName(), clazz.getCanonicalName()));
468458
} else {
469-
code.append(String.format("%s.%s", clazz.getCanonicalName(), ctor.staticMethodName));
459+
if (ctor.staticMethodName == null) {
460+
code.append(String.format("new %s", clazz.getCanonicalName()));
461+
} else {
462+
code.append(String.format("%s.%s", clazz.getCanonicalName(), ctor.staticMethodName));
463+
}
470464
}
471465
List<Binding> params = ctor.parameters;
472-
appendInvocation(code, params);
466+
if (ctor.objectFactory == null) {
467+
appendInvocation(code, params);
468+
}
473469
if (ctor.parameters.isEmpty()) {
474470
// nothing to bind, safe to reuse existing set
475471
code.append(String.format(" : (%s)com.jsoniter.CodegenAccess.resetExistingObject(iter))", clazz.getCanonicalName()));

src/main/java/com/jsoniter/DynamicCodegen.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
11
package com.jsoniter;
22

33
import com.jsoniter.spi.Decoder;
4-
import com.jsoniter.spi.JsoniterSpi;
54
import javassist.ClassPool;
65
import javassist.CtClass;
76
import javassist.CtMethod;
87
import javassist.CtNewMethod;
98

10-
import java.lang.reflect.Type;
11-
129
class DynamicCodegen {
1310

1411
static ClassPool pool = ClassPool.getDefault();

src/main/java/com/jsoniter/JsonIterator.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,12 +293,29 @@ public final Object read() throws IOException {
293293
}
294294
}
295295

296+
/**
297+
* try to bind to existing object, returned object might not the same instance
298+
*
299+
* @param existingObject the object instance to reuse
300+
* @param <T> object type
301+
* @return data binding result, might not be the same object
302+
* @throws IOException if I/O went wrong
303+
*/
296304
public final <T> T read(T existingObject) throws IOException {
297305
this.existingObject = existingObject;
298306
Class<?> clazz = existingObject.getClass();
299307
return (T) Codegen.getDecoder(TypeLiteral.create(clazz).getDecoderCacheKey(), clazz).decode(this);
300308
}
301309

310+
/**
311+
* try to bind to existing object, returned object might not the same instance
312+
*
313+
* @param typeLiteral the type object
314+
* @param existingObject the object instance to reuse
315+
* @param <T> object type
316+
* @return data binding result, might not be the same object
317+
* @throws IOException if I/O went wrong
318+
*/
302319
public final <T> T read(TypeLiteral<T> typeLiteral, T existingObject) throws IOException {
303320
this.existingObject = existingObject;
304321
return (T) Codegen.getDecoder(typeLiteral.getDecoderCacheKey(), typeLiteral.getType()).decode(this);

src/main/java/com/jsoniter/ReflectionObjectDecoder.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ private final void init(Class clazz) throws Exception {
3636
addBinding(clazz, param);
3737
}
3838
this.desc = desc;
39-
if (desc.ctor.ctor == null && desc.ctor.staticFactory == null) {
39+
if (desc.ctor.objectFactory == null && desc.ctor.ctor == null && desc.ctor.staticFactory == null) {
4040
throw new JsonException("no constructor for: " + desc.clazz);
4141
}
4242
for (Binding field : desc.fields) {
@@ -413,6 +413,9 @@ private Object createNewObject(JsonIterator iter, Object[] temp) throws Exceptio
413413
}
414414

415415
private Object createNewObject(Object... args) throws Exception {
416+
if (desc.ctor.objectFactory != null) {
417+
return desc.ctor.objectFactory.create(desc.clazz);
418+
}
416419
if (desc.ctor.staticFactory != null) {
417420
return desc.ctor.staticFactory.invoke(null, args);
418421
} else {

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@ public class ConstructorDescriptor {
1111
* otherwise use static method
1212
*/
1313
public String staticMethodName;
14-
// optional
14+
// option 1: use constructor
1515
public Constructor ctor;
16-
// optional
16+
// option 2: use static method
1717
public Method staticFactory;
18+
// option 3: create by extension
19+
public Extension objectFactory;
1820

1921
/**
2022
* the parameters to call constructor or static method

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,16 @@ public Type chooseImplementation(Type type) {
99
return type;
1010
}
1111

12+
@Override
13+
public boolean canCreate(Class clazz) {
14+
return false;
15+
}
16+
17+
@Override
18+
public Object create(Class clazz) {
19+
throw new UnsupportedOperationException();
20+
}
21+
1222
@Override
1323
public Decoder createDecoder(String cacheKey, Type type) {
1424
return null;

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,22 @@ public interface Extension {
1111
*/
1212
Type chooseImplementation(Type type);
1313

14+
/**
15+
* Can this extension create object instance for given interface type
16+
*
17+
* @param clazz the interface
18+
* @return true if can create, false if can not
19+
*/
20+
boolean canCreate(Class clazz);
21+
22+
/**
23+
* Create object instance for given interface type
24+
*
25+
* @param clazz the interface
26+
* @return the object instance, throw exception if can not create
27+
*/
28+
Object create(Class clazz);
29+
1430
/**
1531
* Customize type decoding
1632
*
@@ -20,6 +36,8 @@ public interface Extension {
2036
*/
2137
Decoder createDecoder(String cacheKey, Type type);
2238

39+
// TODO: add createEncoder
40+
2341
/**
2442
* Update binding is done for the class
2543
*

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ public class JsoniterSpi {
1111
static Map<Class, Class> typeImpls = new HashMap<Class, Class>();
1212
static volatile Map<String, Encoder> encoders = new HashMap<String, Encoder>();
1313
static volatile Map<String, Decoder> decoders = new HashMap<String, Decoder>();
14+
static volatile Map<Class, Extension> objectFactories = new HashMap<Class, Extension>();
1415

1516
public static void registerExtension(Extension extension) {
1617
extensions.add(extension);
@@ -80,6 +81,29 @@ public synchronized static void addNewEncoder(String cacheKey, Encoder encoder)
8081
encoders = newCache;
8182
}
8283

84+
public static boolean canCreate(Class clazz) {
85+
if (objectFactories.containsKey(clazz)) {
86+
return true;
87+
}
88+
for (Extension extension : extensions) {
89+
if (extension.canCreate(clazz)) {
90+
addObjectFactory(clazz, extension);
91+
return true;
92+
}
93+
}
94+
return false;
95+
}
96+
97+
public static Object create(Class clazz) {
98+
return objectFactories.get(clazz).create(clazz);
99+
}
100+
101+
private synchronized static void addObjectFactory(Class clazz, Extension extension) {
102+
HashMap<Class, Extension> copy = new HashMap<Class, Extension>(objectFactories);
103+
copy.put(clazz, extension);
104+
objectFactories = copy;
105+
}
106+
83107
public static ClassDescriptor getDecodingClassDescriptor(Class clazz, boolean includingPrivate) {
84108
Map<String, Type> lookup = collectTypeVariableLookup(clazz);
85109
ClassDescriptor desc = new ClassDescriptor();
@@ -236,6 +260,10 @@ private static void encodingDeduplicate(ClassDescriptor desc) {
236260

237261
private static ConstructorDescriptor getCtor(Class clazz) {
238262
ConstructorDescriptor cctor = new ConstructorDescriptor();
263+
if (canCreate(clazz)) {
264+
cctor.objectFactory = objectFactories.get(clazz);
265+
return cctor;
266+
}
239267
try {
240268
cctor.ctor = clazz.getDeclaredConstructor();
241269
} catch (Exception e) {

src/test/java/com/jsoniter/TestObject.java

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

33
import com.jsoniter.any.Any;
4+
import com.jsoniter.spi.EmptyExtension;
5+
import com.jsoniter.spi.JsoniterSpi;
46
import junit.framework.TestCase;
57

68
import java.io.IOException;
@@ -12,7 +14,8 @@ public class TestObject extends TestCase {
1214
// JsonIterator.setMode(DecodingMode.DYNAMIC_MODE_AND_MATCH_FIELD_WITH_HASH);
1315
}
1416

15-
public static class EmptyClass {}
17+
public static class EmptyClass {
18+
}
1619

1720
public void test_empty_class() throws IOException {
1821
JsonIterator iter = JsonIterator.parse("{}");
@@ -26,8 +29,8 @@ public void test_empty_object() throws IOException {
2629
SimpleObject simpleObj = iter.read(SimpleObject.class);
2730
assertNull(simpleObj.field1);
2831
iter.reset(iter.buf);
29-
Map obj = (Map) iter.read(Object.class);
30-
assertEquals(0, obj.size());
32+
Object obj = iter.read(Object.class);
33+
assertEquals(0, ((Map)obj).size());
3134
iter.reset(iter.buf);
3235
Any any = iter.readAny();
3336
assertEquals(0, any.size());
@@ -103,4 +106,37 @@ public void test_incomplete_field_name() throws IOException {
103106
} catch (JsonException e) {
104107
}
105108
}
109+
110+
public static interface IDependenceInjectedObject {
111+
String getSomeService();
112+
}
113+
114+
public static class DependenceInjectedObject implements IDependenceInjectedObject {
115+
116+
private String someService;
117+
118+
public DependenceInjectedObject(String someService) {
119+
this.someService = someService;
120+
}
121+
122+
public String getSomeService() {
123+
return someService;
124+
}
125+
}
126+
127+
public void test_object_creation() throws IOException {
128+
JsoniterSpi.registerExtension(new EmptyExtension() {
129+
@Override
130+
public boolean canCreate(Class clazz) {
131+
return clazz.equals(DependenceInjectedObject.class) || clazz.equals(IDependenceInjectedObject.class);
132+
}
133+
134+
@Override
135+
public Object create(Class clazz) {
136+
return new DependenceInjectedObject("hello");
137+
}
138+
});
139+
IDependenceInjectedObject obj = JsonIterator.deserialize("{}", IDependenceInjectedObject.class);
140+
assertEquals("hello", obj.getSomeService());
141+
}
106142
}

0 commit comments

Comments
 (0)