alibaba / fastjson

FASTJSON 2.0.x has been released, faster and more secure, recommend you upgrade.
https://github.com/alibaba/fastjson2/wiki/fastjson_1_upgrade_cn
Apache License 2.0
25.74k stars 6.5k forks source link

多层嵌套基本类型数组反序列化时报空指针异常 #912

Closed dengyj3 closed 7 years ago

dengyj3 commented 7 years ago

数据结构如下: JsonBean下面有一个List, SCFMethod对象中又有一个List对象 SCFParameter中有一个Class<?> clazz clazz为一个基本类型数组,如int[], float[], short[],double[],long[],byte[],char[] 反序列化时报错: java.lang.reflect.InvocationTargetException at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at com.wf.mvc.invoke.ActionInvoker.invoke(ActionInvoker.java:76) at com.wf.mvc.MvcDispatcher.service(MvcDispatcher.java:121) at com.wf.mvc.MvcFilter.doFilter(MvcFilter.java:67) at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1482) at org.jasig.cas.client.util.AssertionThreadLocalFilter.doFilter(AssertionThreadLocalFilter.java:50) at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1482) at org.jasig.cas.client.util.HttpServletRequestWrapperFilter.doFilter(HttpServletRequestWrapperFilter.java:71) at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1482) at com.sso.client.AbstractTicketValidationFilter.doFilter(AbstractTicketValidationFilter.java:258) at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1482) at com.sso.client.AuthenticationFilter.doFilter(AuthenticationFilter.java:161) at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1482) at com.sso.client.SingleSignOutFilter.doFilter(SingleSignOutFilter.java:89) at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1482) at com.sso.client.CharacterEncodingFilter.doFilter(CharacterEncodingFilter.java:37) at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1482) at com.sso.session.servlet.filter.session.CacheSessionFilter.doFilter(CacheSessionFilter.java:94) at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1474) at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:499) at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:137) at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:557) at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:231) at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1086) at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:428) at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:193) at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1020) at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:135) at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:255) at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:154) at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:116) at org.eclipse.jetty.server.Server.handle(Server.java:370) at org.eclipse.jetty.server.AbstractHttpConnection.handleRequest(AbstractHttpConnection.java:489) at org.eclipse.jetty.server.AbstractHttpConnection.content(AbstractHttpConnection.java:960) at org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.content(AbstractHttpConnection.java:1021) at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:865) at org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:240) at org.eclipse.jetty.server.AsyncHttpConnection.handle(AsyncHttpConnection.java:82) at org.eclipse.jetty.io.nio.SelectChannelEndPoint.handle(SelectChannelEndPoint.java:668) at org.eclipse.jetty.io.nio.SelectChannelEndPoint$1.run(SelectChannelEndPoint.java:52) at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:608) at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:543) at java.lang.Thread.run(Thread.java:745) Caused by: com.alibaba.fastjson.JSONException at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:625) at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:593) at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialzeArrayMapping(JavaBeanDeserializer.java:244) at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:308) at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:189) at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:185) at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.parseArray(JavaBeanDeserializer.java:895) at com.alibaba.fastjson.parser.deserializer.FastjsonASMDeserializer_2_SCFMethod.deserialzeArrayMapping(Unknown Source) at com.alibaba.fastjson.parser.deserializer.FastjsonASMDeserializer_2_SCFMethod.deserialze(Unknown Source) at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:185) at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.parseArray(JavaBeanDeserializer.java:895) at com.alibaba.fastjson.parser.deserializer.FastjsonASMDeserializer_1_JsonBean.deserialzeArrayMapping(Unknown Source) at com.alibaba.fastjson.parser.deserializer.FastjsonASMDeserializer_1_JsonBean.deserialze(Unknown Source) at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:185) at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:621) at com.alibaba.fastjson.JSON.parseObject(JSON.java:339) at com.alibaba.fastjson.JSON.parseObject(JSON.java:243) at com.tfci.interfaceInfo.GetInterfaceImpl.getJsonData(GetInterfaceImpl.java:378) at com.tfci.controllers.CallJarServiceController.getMethods(CallJarServiceController.java:153) ... 46 more Caused by: java.lang.NullPointerException at java.lang.reflect.Array.newArray(Native Method) at java.lang.reflect.Array.newInstance(Array.java:70) at com.alibaba.fastjson.util.TypeUtils.loadClass(TypeUtils.java:994) at com.alibaba.fastjson.serializer.MiscCodec.deserialze(MiscCodec.java:295) at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:621) ... 64 more

经过分析: 如果是int[]数组,转换成class对象是"[I",在TypeUtils.java中993行 Class<?> componentType = loadClass(className.substring(1), classLoader); 执行完className.substring(1)后为"I", 从而loadClass("I",classLoader)方法时执行到1028行: clazz = Class.forName(className);时由于找不到“I”类而抛了异常,导致反序列化失败; 在源码992行的if中 增加判断if (className.charAt(0) == '[') { String cls = className.substring(1); Class<?> componentType; if(cls.equals("I")){ componentType = loadClass("int", classLoader); }else if(cls.equals("F")){ componentType = loadClass("float", classLoader); }else if(cls.equals("S")){ componentType = loadClass("short", classLoader); }else if(cls.equals("D")){ componentType = loadClass("double", classLoader); }else if(cls.equals("J")){ componentType = loadClass("long", classLoader); }else if(cls.equals("B")){ componentType = loadClass("byte", classLoader); }else if(cls.equals("C")){ componentType = loadClass("char", classLoader); }else{ componentType = loadClass(className.substring(1), classLoader); } return Array.newInstance(componentType, 0).getClass(); } 反序列化正常通过。 版本:1.2.21 反序列化的字符串为: {"mList":[{"className":"com.qa.scftemplate.contract.ISCFServiceForDyjAction","methodName":"getEntity","parameterSize":1,"parameters":[{"clazz":"java.util.List","clsList":null,"isGenericity":false,"value":""}],"returnType":"com.qa.scftemplate.entity.Entity","url":"tcp://SCFServiceForDyj/SCFServiceForDyjActionService"},{"className":"com.qa.scftemplate.contract.ISCFServiceForDyjAction","methodName":"getMapEntity","parameterSize":2,"parameters":[{"clazz":"java.util.Map","clsList":null,"isGenericity":false,"value":""},{"clazz":"int","clsList":null,"isGenericity":false,"value":""}],"returnType":"java.lang.String","url":"tcp://SCFServiceForDyj/SCFServiceForDyjActionService"},{"className":"com.qa.scftemplate.contract.ISCFServiceForDyjAction","methodName":"getTextString","parameterSize":2,"parameters":[{"clazz":"java.util.List","clsList":null,"isGenericity":false,"value":""},{"clazz":"java.lang.String","clsList":null,"isGenericity":false,"value":""}],"returnType":"java.lang.String","url":"tcp://SCFServiceForDyj/SCFServiceForDyjActionService"},{"className":"com.qa.scftemplate.contract.ISCFServiceForDyjAction","methodName":"getArrayInt","parameterSize":1,"parameters":[{"clazz":"[I","clsList":null,"isGenericity":false,"value":""}],"returnType":"[I","url":"tcp://SCFServiceForDyj/SCFServiceForDyjActionService"},{"className":"com.qa.scftemplate.contract.ISCFServiceForDyjAction","methodName":"getArrayInt","parameterSize":1,"parameters":[{"clazz":"[Ljava.lang.Integer;","clsList":null,"isGenericity":false,"value":""}],"returnType":"[Ljava.lang.Integer;","url":"tcp://SCFServiceForDyj/SCFServiceForDyjActionService"},{"className":"com.qa.scftemplate.contract.ISCFServiceForDyjAction","methodName":"getSetEntity","parameterSize":2,"parameters":[{"clazz":"java.util.Set","clsList":null,"isGenericity":false,"value":""},{"clazz":"int","clsList":null,"isGenericity":false,"value":""}],"returnType":"java.lang.String","url":"tcp://SCFServiceForDyj/SCFServiceForDyjActionService"},{"className":"com.qa.scftemplate.contract.ISCFServiceForDyjAction","methodName":"returnJsonString","parameterSize":1,"parameters":[{"clazz":"com.qa.scftemplate.entity.Entity","clsList":null,"isGenericity":false,"value":""}],"returnType":"java.lang.String","url":"tcp://SCFServiceForDyj/SCFServiceForDyjActionService"},{"className":"com.qa.scftemplate.contract.ISCFServiceForDyjAction","methodName":"getEntityString","parameterSize":1,"parameters":[{"clazz":"java.util.List","clsList":null,"isGenericity":false,"value":""}],"returnType":"java.lang.String","url":"tcp://SCFServiceForDyj/SCFServiceForDyjActionService"},{"className":"com.qa.scftemplate.contract.ISCFServiceForDyjAction","methodName":"getArrayEntity","parameterSize":2,"parameters":[{"clazz":"[Lcom.qa.scftemplate.entity.Entity;","clsList":null,"isGenericity":false,"value":""},{"clazz":"int","clsList":null,"isGenericity":false,"value":""}],"returnType":"[Lcom.qa.scftemplate.entity.Entity;","url":"tcp://SCFServiceForDyj/SCFServiceForDyjActionService"},{"className":"com.qa.scftemplate.contract.ISCFServiceForDyjAction","methodName":"getArrayString","parameterSize":1,"parameters":[{"clazz":"[Ljava.lang.String;","clsList":null,"isGenericity":false,"value":""}],"returnType":"[Ljava.lang.String;","url":"tcp://SCFServiceForDyj/SCFServiceForDyjActionService"},{"className":"com.qa.scftemplate.contract.ISCFServiceForDyjAction","methodName":"getArrayPrimative","parameterSize":7,"parameters":[{"clazz":"[I","clsList":null,"isGenericity":false,"value":""},{"clazz":"[F","clsList":null,"isGenericity":false,"value":""},{"clazz":"[S","clsList":null,"isGenericity":false,"value":""},{"clazz":"[D","clsList":null,"isGenericity":false,"value":""},{"clazz":"[J","clsList":null,"isGenericity":false,"value":""},{"clazz":"[B","clsList":null,"isGenericity":false,"value":""},{"clazz":"[C","clsList":null,"isGenericity":false,"value":""}],"returnType":"[Ljava.lang.String;","url":"tcp://SCFServiceForDyj/SCFServiceForDyjActionService"}]} 请问是不是fastJson中的bug或是我反序列化时需要加什么参数?

dengyj3 commented 7 years ago

反序列化时的方法封装如下:
//JsonString to JavaBean public static T getJsonData(String json, Class clazz){ T jd=(T)JSON.parseObject(json, clazz, Feature.IgnoreNotMatch, Feature.AutoCloseSource, Feature.UseObjectArray//, //Feature.SupportArrayToBean ); return jd; }

githublaohu commented 7 years ago

如果我是作者,一定不维护,这样的工作。真心变态啊。

wenshao commented 7 years ago

能帮忙构建一个重现问题的testcase么?

wenshao commented 7 years ago

@dengyj3 已经支持,但是没有重现问题的testcase,无法证明是否已经解决问题

dengyj3 commented 7 years ago

1、String串为如下:

String allMethods = "{\"mList\":[{\"className\":\"com.qa.scftemplate.contract.ISCFServiceForDyjAction\",\"methodName\":\"getArrayInt\",\"parameterSize\":1,\"parameters\":[{\"clazz\":\"[I\",\"clsList\":null,\"isGenericity\":false,\"value\":\"\"}],\"returnType\":\"[I\",\"url\":\"tcp://SCFServiceForDyj/SCFServiceForDyjActionService\"},{\"className\":\"com.qa.scftemplate.contract.ISCFServiceForDyjAction\",\"methodName\":\"getArrayPrimative\",\"parameterSize\":7,\"parameters\":[{\"clazz\":\"[I\",\"clsList\":null,\"isGenericity\":false,\"value\":\"\"},{\"clazz\":\"[F\",\"clsList\":null,\"isGenericity\":false,\"value\":\"\"},{\"clazz\":\"[S\",\"clsList\":null,\"isGenericity\":false,\"value\":\"\"},{\"clazz\":\"[D\",\"clsList\":null,\"isGenericity\":false,\"value\":\"\"},{\"clazz\":\"[J\",\"clsList\":null,\"isGenericity\":false,\"value\":\"\"},{\"clazz\":\"[B\",\"clsList\":null,\"isGenericity\":false,\"value\":\"\"},{\"clazz\":\"[C\",\"clsList\":null,\"isGenericity\":false,\"value\":\"\"}],\"returnType\":\"[Ljava.lang.String;\",\"url\":\"tcp://SCFServiceForDyj/SCFServiceForDyjActionService\"}]}";

2、解析如下:

JsonBean jsonBean = getJsonData(allMethods, JsonBean.class);

3、getJsonData方法如下:

    public static <T> T getJsonData(String json, Class<T> clazz){
        T jd=(T)JSON.parseObject(json, clazz,
                Feature.IgnoreNotMatch,
                Feature.AutoCloseSource
                );
        return jd;
    }

4、JsonBean类如下:

import java.util.List;
import com.tfci.interfaceInfo.entity.SCFMethod;
public class JsonBean {
    private List<SCFMethod> mList;
    public List<SCFMethod> getmList() {
        return mList;
    }
    public void setmList(List<SCFMethod> mList) {
        this.mList = mList;
    }
}

5、SCFMethod如下:

import java.util.LinkedList;
import java.util.List;
public class SCFMethod  {
    public String className;
    public String url;
    public String methodName;
    public int parameterSize;
    public List<SCFMethodParameter> parameters = new LinkedList<SCFMethodParameter>();
    public Class<?> returnType; 
    public String getUrl() {
        return url;
    }
    public void setUrl(String url) {
        this.url = url;
    }
    public String getClassName() {
        return className;
    }
    public void setClassName(String className) {
        this.className = className;
    }
    public Class<?> getReturnType() {
        return returnType;
    }
    public void setReturnType(Class<?> returnType) {
        this.returnType = returnType;
    }
    public String getMethodName() {
        return methodName;
    }
    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }
    public int getParameterSize() {
        return parameterSize;
    }
    public void setParameterSize(int parameterSize) {
        this.parameterSize = parameterSize;
    }
    public List<SCFMethodParameter> getParameters() {
        return parameters;
    }
    public void setParameters(List<SCFMethodParameter> parameters) {
        this.parameters = parameters;
    }
}

6、SCFMethodParameter类定义如下:

import java.util.List;
public class SCFMethodParameter implements Cloneable{
    public Class<?> clazz;
    public Object value;
    public boolean isGenericity = false;
    public List<Class<?>> clsList;
    public boolean getIsGenericity() {
        return isGenericity;
    }
    public void setIsGenericity(boolean isGenericity) {
        this.isGenericity = isGenericity;
    }
    public Object getValue() {
        return value;
    }
    public void setValue(Object value) {
        this.value = value;
    }
    public Class<?> getClazz() {
        return clazz;
    }
    public void setClazz(Class<?> clazz) {
        this.clazz = clazz;
    }
    public List<Class<?>> getClsList() {
        return clsList;
    }
    public void setClsList(List<Class<?>> clsList) {
        this.clsList = clsList;
    }
}

7、测试类即把上述1、2放到main方法中执行,报错:

cyberdak commented 7 years ago

这个case我跑起来没问题,甚至是用1.2.0跑都没问题.

我把case 放到 https://github.com/cyberdak/fastjson-issue 这个项目里面了. @dengyj3 有空看看我的case对不对。你复制到github上面的代码不对,我自己补全了一下。

dengyj3 commented 7 years ago

@cyberdak,您好,谢谢你的回复, 我才发现提交的代码中看不到泛型,只有在编辑模式下才能看到 1、按你的case跑起来确实没问题,但是我的JsonBean.java中是用到泛型,即List中是SCFMethod对象(即private List mList;),你的类中未加具体的泛型对象,我把你的类修改如下: import java.util.List;

public class JsonBean { private List mList; public List getmList() { return mList; } public void setmList(List mList) { this.mList = mList; } @Override public String toString() { return "JsonBean [mList=" + mList + "]"; } } 我用你的代码,加上泛型参数,同样报错: Exception in thread "main" com.alibaba.fastjson.JSONException at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:625) at com.alibaba.fastjson.JSON.parseObject(JSON.java:339) at com.alibaba.fastjson.JSON.parseObject(JSON.java:243) at fastjson.test.SCFTest.getJsonData(SCFTest.java:15) at fastjson.test.SCFTest.main(SCFTest.java:10) Caused by: java.lang.NullPointerException at java.lang.reflect.Array.newArray(Native Method) at java.lang.reflect.Array.newInstance(Array.java:70) at com.alibaba.fastjson.util.TypeUtils.loadClass(TypeUtils.java:994) at com.alibaba.fastjson.serializer.MiscCodec.deserialze(MiscCodec.java:295) at com.alibaba.fastjson.parser.deserializer.DefaultFieldDeserializer.parseField(DefaultFieldDeserializer.java:69) at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:549) at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:189) at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:185) at com.alibaba.fastjson.parser.deserializer.FastjsonASMDeserializer_1_JsonBean.deserialze(Unknown Source) at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:185) at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:621) ... 4 more

2、我复制的代码没有问题,你把泛型都加上试试,我看你的代码中把泛型的具体类都去掉了,在我定义的实体中是有的,难道是因为泛型的问题吗?

3、我把https://github.com/cyberdak/fastjson-issue这里的代码更新了一下,增加了泛型参数,和我本地的代码一致了,看来直接复制代码会把泛型的具体类型会弄丢啊 详见:https://github.com/dengyj3/fastjson-issue/branches

wenshao commented 7 years ago

问题已经重现并且解决,预计周末发布新版本解决问题

wenshao commented 7 years ago

问题已经修复,请使用1.2.22版本