Skip to content
This repository has been archived by the owner on Oct 23, 2024. It is now read-only.

Commit

Permalink
improved beanToArray support
Browse files Browse the repository at this point in the history
  • Loading branch information
wenshao committed Oct 7, 2019
1 parent 0271ecc commit 036012f
Show file tree
Hide file tree
Showing 9 changed files with 297 additions and 1 deletion.
2 changes: 2 additions & 0 deletions src/main/java/com/alibaba/fastjson/parser/JSONLexer.java
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ public interface JSONLexer {

boolean isRef();

String scanTypeName(SymbolTable symbolTable);

String numberString();

byte[] bytesValue();
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/com/alibaba/fastjson/parser/JSONLexerBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -1111,6 +1111,10 @@ && charAt(np + 3) == 'e' //
&& charAt(np + 4) == 'f';
}

public String scanTypeName(SymbolTable symbolTable) {
return null;
}

protected final static char[] typeFieldName = ("\"" + JSON.DEFAULT_TYPE_KEY + "\":\"").toCharArray();

public final int scanType(String type) {
Expand Down
23 changes: 23 additions & 0 deletions src/main/java/com/alibaba/fastjson/parser/JSONScanner.java
Original file line number Diff line number Diff line change
Expand Up @@ -2991,4 +2991,27 @@ public int seekObjectToField(long[] fieldNameHash) {
}
}
}

public String scanTypeName(SymbolTable symbolTable) {
if (text.startsWith("\"@type\":\"", bp)) {
int p = text.indexOf('"', bp + 9);
if (p != -1) {
bp += 9;
int h = 0;
for (int i = bp; i < p; i++) {
h = 31 * h + text.charAt(i);
}
String typeName = addSymbol(bp, p - bp, h, symbolTable);
char separator = text.charAt(p + 1);
if (separator != ',' && separator != ']') {
return null;
}
bp = p + 2;
ch = text.charAt(bp);
return typeName;
}
}

return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -903,6 +903,7 @@ public ObjectDeserializer createJavaBeanDeserializer(Class<?> clazz, Type type)
&& ((!ASMUtils.checkName(annotation.name())) //
|| annotation.format().length() != 0 //
|| annotation.deserializeUsing() != Void.class //
|| annotation.parseFeatures().length != 0 //
|| annotation.unwrapped())
|| (fieldInfo.method != null && fieldInfo.method.getParameterTypes().length > 1)) {
asmEnable = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,41 @@ private void _deserialzeArrayMapping(ClassWriter cw, Context context) {

defineVarLexer(context, mw);

mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 1);
mw.visitMethodInsn(INVOKEVIRTUAL, DefaultJSONParser, "getSymbolTable", "()" + desc(SymbolTable.class));
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanTypeName", "(" + desc(SymbolTable.class) + ")Ljava/lang/String;");
mw.visitVarInsn(ASTORE, context.var("typeName"));

Label typeNameNotNull_ = new Label();
mw.visitVarInsn(ALOAD, context.var("typeName"));
mw.visitJumpInsn(IFNULL, typeNameNotNull_);

mw.visitVarInsn(ALOAD, 1);
mw.visitMethodInsn(INVOKEVIRTUAL, DefaultJSONParser, "getConfig", "()" + desc(ParserConfig.class));
mw.visitVarInsn(ALOAD, 0);
mw.visitFieldInsn(GETFIELD, type(JavaBeanDeserializer.class), "beanInfo", desc(JavaBeanInfo.class));
mw.visitVarInsn(ALOAD, context.var("typeName"));
mw.visitMethodInsn(INVOKESTATIC, type(JavaBeanDeserializer.class), "getSeeAlso"
, "(" + desc(ParserConfig.class) + desc(JavaBeanInfo.class) + "Ljava/lang/String;)" + desc(JavaBeanDeserializer.class));
mw.visitVarInsn(ASTORE, context.var("userTypeDeser"));
mw.visitVarInsn(ALOAD, context.var("userTypeDeser"));
mw.visitTypeInsn(INSTANCEOF, type(JavaBeanDeserializer.class));
mw.visitJumpInsn(IFEQ, typeNameNotNull_);

mw.visitVarInsn(ALOAD, context.var("userTypeDeser"));
mw.visitVarInsn(ALOAD, Context.parser);
mw.visitVarInsn(ALOAD, 2);
mw.visitVarInsn(ALOAD, 3);
mw.visitVarInsn(ALOAD, 4);
mw.visitMethodInsn(INVOKEVIRTUAL, //
type(JavaBeanDeserializer.class), //
"deserialzeArrayMapping", //
"(L" + DefaultJSONParser + ";Ljava/lang/reflect/Type;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
mw.visitInsn(ARETURN);

mw.visitLabel(typeNameNotNull_);

_createInstance(context, mw);

FieldInfo[] sortedFieldInfoList = context.beanInfo.sortedFields;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,22 @@ public <T> T deserialzeArrayMapping(DefaultJSONParser parser, Type type, Object
throw new JSONException("error");
}

String typeName = null;
if ((typeName = lexer.scanTypeName(parser.symbolTable)) != null) {
ObjectDeserializer deserializer = getSeeAlso(parser.getConfig(), this.beanInfo, typeName);
Class<?> userType = null;

if (deserializer == null) {
Class<?> expectClass = TypeUtils.getClass(type);
userType = parser.getConfig().checkAutoType(typeName, expectClass, lexer.getFeatures());
deserializer = parser.getConfig().getDeserializer(userType);
}

if (deserializer instanceof JavaBeanDeserializer) {
return ((JavaBeanDeserializer) deserializer).deserialzeArrayMapping(parser, type, fieldName, object);
}
}

object = createInstance(parser, type);

for (int i = 0, size = sortedFieldDeserializers.length; i < size; ++i) {
Expand Down Expand Up @@ -1541,7 +1557,7 @@ protected Object parseRest(DefaultJSONParser parser
return value;
}

protected JavaBeanDeserializer getSeeAlso(ParserConfig config, JavaBeanInfo beanInfo, String typeName) {
protected static JavaBeanDeserializer getSeeAlso(ParserConfig config, JavaBeanInfo beanInfo, String typeName) {
if (beanInfo.jsonType == null) {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package com.alibaba.json.bvt.parser.array;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.annotation.JSONField;
import com.alibaba.fastjson.annotation.JSONType;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.serializer.SerializerFeature;
import junit.framework.TestCase;

public class BeanToArrayAutoTypeTest extends TestCase {
public void test_for_issue_x() throws Exception {
String json = "[\"@type\":\"B\",\"chengchao\",1001]";
A a = JSON.parseObject(json, A.class, Feature.SupportAutoType, Feature.SupportArrayToBean);
B b = (B) a;
}

public void test_for_issue() throws Exception {
Model m = new Model();
m.value = new B(1001, "chengchao");
String json = JSON.toJSONString(m);
assertEquals("{\"value\":[\"@type\":\"B\",\"chengchao\",1001]}", json);

Model m1 = JSON.parseObject(json, Model.class, Feature.SupportAutoType);
assertEquals(m.value.getClass(), m1.value.getClass());
assertEquals(json, JSON.toJSONString(m1));
}

public void test_for_issue_1() throws Exception {
Model m = new Model();
m.value = new C(1001, 58);
String json = JSON.toJSONString(m);
assertEquals("{\"value\":[\"@type\":\"C\",58,1001]}", json);

Model m1 = JSON.parseObject(json, Model.class, Feature.SupportAutoType);
assertEquals(m.value.getClass(), m1.value.getClass());
assertEquals(json, JSON.toJSONString(m1));
}

@JSONType(seeAlso = {B.class, C.class})
public static class A {
protected int id;

public int getId()
{
return id;
}

public void setId(int id)
{
this.id = id;
}
}

@JSONType(typeName = "B", orders = {"name", "id"})
public static class B extends A {
private String name;

public B() {

}

public B(int id, String name) {
this.id = id;
this.name = name;
}

public String getName()
{
return name;
}

public void setName(String name)
{
this.name = name;
}
}

@JSONType(typeName = "C", orders = {"age", "id"})
public static class C extends A {
public int age;

public C() {

}

public C(int id, int age) {
this.id = id;
this.age = age;
}
}

public static class Model {
@JSONField(serialzeFeatures = {SerializerFeature.BeanToArray, SerializerFeature.WriteClassName}
, parseFeatures = Feature.SupportArrayToBean)
public A value;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.alibaba.json.bvt.parser.array;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.annotation.JSONField;
import com.alibaba.fastjson.annotation.JSONType;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
import junit.framework.TestCase;

public class BeanToArrayAutoTypeTest2
extends TestCase {
public void test_for_issue() throws Exception {
Model m = new Model();
m.value = new B(1001, "chengchao");
String json = JSON.toJSONString(m);
assertEquals("{\"value\":[\"@type\":\"B\",\"chengchao\",1001]}", json);

Model m1 = JSON.parseObject(json, Model.class, Feature.SupportAutoType);
assertEquals(m.value.getClass(), m1.value.getClass());
assertEquals(json, JSON.toJSONString(m1));
}

public void test_for_issue_1() throws Exception {
Model m = new Model();
m.value = new C(1001, 58);
String json = JSON.toJSONString(m);
assertEquals("{\"value\":[\"@type\":\"C\",58,1001]}", json);

Model m1 = JSON.parseObject(json, Model.class, Feature.SupportAutoType);
assertEquals(m.value.getClass(), m1.value.getClass());
assertEquals(json, JSON.toJSONString(m1));
}

@JSONType(seeAlso = {B.class, C.class}
, serialzeFeatures = {SerializerFeature.BeanToArray, SerializerFeature.WriteClassName}
, parseFeatures = Feature.SupportArrayToBean)
public static class A {
public int id;
}

@JSONType(typeName = "B", orders = {"name", "id"}
, serialzeFeatures = {SerializerFeature.BeanToArray, SerializerFeature.WriteClassName}
, parseFeatures = Feature.SupportArrayToBean)
public static class B extends A {
public String name;

public B() {

}

public B(int id, String name) {
this.id = id;
this.name = name;
}
}

@JSONType(typeName = "C", orders = {"age", "id"}
, serialzeFeatures = {SerializerFeature.BeanToArray, SerializerFeature.WriteClassName}
, parseFeatures = Feature.SupportArrayToBean)
public static class C extends A {
public int age;

public C() {

}

public C(int id, int age) {
this.id = id;
this.age = age;
}
}

public static class Model {
public A value;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.alibaba.json.bvt.parser.array;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.annotation.JSONType;
import com.alibaba.fastjson.parser.Feature;
import junit.framework.TestCase;

import java.util.List;

public class BeanToArrayAutoTypeTest3
extends TestCase {
public void test_beanToArray() throws Exception {
Topology topology = JSON.parseObject("{\"maps\":[[\"@type\":\"Log\"]]}", Topology.class);
assertEquals(LogSourceMeta.class, topology.maps.get(0).getClass());
}

public void test_beanToArray1() throws Exception {
Topology topology = JSON.parseObject("{\"maps\":[[\"@type\":\"Log\",123]]}", Topology.class);
assertEquals(LogSourceMeta.class, topology.maps.get(0).getClass());
assertEquals(123, ((LogSourceMeta) topology.maps.get(0)).id);
}

@JSONType(typeName = "Log")
public static class LogSourceMeta extends MapTaskMeta {
public int id;
}

@JSONType(seeAlso = {LogSourceMeta.class, OtherMeta.class}, parseFeatures = Feature.SupportArrayToBean)
public static class MapTaskMeta {

}

@JSONType(typeName = "Other")
public static class OtherMeta extends MapTaskMeta {

}

public static class Topology {
public List<MapTaskMeta> maps;
}
}

0 comments on commit 036012f

Please sign in to comment.