[alibaba/fastjson]反序列化时把json字符串里的null反序列化为List.of(null)

2024-10-16 846 views
0

单元测试如下:

public class JsonDeserializeTest {
    @Test
    public void test() {
        String data = "{\"field1\": null, \"field2\": null, \"field3\": \"1\", \"field4\": \"null\"}";

        Model model;
        try {
            model = JSON.parseObject(data, Model.class);
        } catch (Exception e) {
            throw new IllegalStateException("parse " + data + " to object failed.", e);
        }
        Assert.assertEquals(model.field1, 0);
        Assert.assertEquals(model.field2, 0F);
        Assert.assertEquals(model.field3, "1");
        Assert.assertNull(model.field4);
        Assert.assertEquals(model.field5, 10000);
    }

    public static class Model {
        public final int field1;
        public final float field2;
        public final String field3;
        public final List<String> field4;
        public int field5 = 0;

        public Model(int field1, float field2, String field3, List<String> field4) {
            this.field1 = field1;
            this.field2 = field2;
            this.field3 = field3;
            this.field4 = field4;
            this.field5 = 10000;
        }
    }
}

报错如下:

java.lang.AssertionError: expected [null] but found [[null]]
Expected :null
Actual   :[null]

    at org.testng.Assert.fail(Assert.java:96)
    at org.testng.Assert.failNotSame(Assert.java:772)
    at org.testng.Assert.assertNull(Assert.java:708)
    at org.testng.Assert.assertNull(Assert.java:697)
    at unittest.gs.framework.JsonDeserializeTest.test5(JsonDeserializeTest.java:100)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:124)
    at org.testng.internal.Invoker.invokeMethod(Invoker.java:583)
    at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:719)
    at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:989)
    at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:125)
    at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:109)
    at org.testng.TestRunner.privateRun(TestRunner.java:648)
    at org.testng.TestRunner.run(TestRunner.java:505)
    at org.testng.SuiteRunner.runTest(SuiteRunner.java:455)
    at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:450)
    at org.testng.SuiteRunner.privateRun(SuiteRunner.java:415)
    at org.testng.SuiteRunner.run(SuiteRunner.java:364)
    at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
    at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:84)
    at org.testng.TestNG.runSuitesSequentially(TestNG.java:1208)
    at org.testng.TestNG.runSuitesLocally(TestNG.java:1137)
    at org.testng.TestNG.runSuites(TestNG.java:1049)
    at org.testng.TestNG.run(TestNG.java:1017)
    at com.intellij.rt.testng.IDEARemoteTestNG.run(IDEARemoteTestNG.java:66)
    at com.intellij.rt.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java:110)

回答

7

期望的结果应该是反序列化失败; 如果把null的双引号去掉,参数为空会报另一个错;

java.lang.IllegalStateException: parse {"field1": null, "field2": null, "field3": "1", "field4": null} to object failed.

    at unittest.gs.framework.JsonDeserializeTest.test5(JsonDeserializeTest.java:105)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:124)
    at org.testng.internal.Invoker.invokeMethod(Invoker.java:583)
    at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:719)
    at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:989)
    at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:125)
    at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:109)
    at org.testng.TestRunner.privateRun(TestRunner.java:648)
    at org.testng.TestRunner.run(TestRunner.java:505)
    at org.testng.SuiteRunner.runTest(SuiteRunner.java:455)
    at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:450)
    at org.testng.SuiteRunner.privateRun(SuiteRunner.java:415)
    at org.testng.SuiteRunner.run(SuiteRunner.java:364)
    at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
    at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:84)
    at org.testng.TestNG.runSuitesSequentially(TestNG.java:1208)
    at org.testng.TestNG.runSuitesLocally(TestNG.java:1137)
    at org.testng.TestNG.runSuites(TestNG.java:1049)
    at org.testng.TestNG.run(TestNG.java:1017)
    at com.intellij.rt.testng.IDEARemoteTestNG.run(IDEARemoteTestNG.java:66)
    at com.intellij.rt.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java:110)
Caused by: com.alibaba.fastjson.JSONException: set property error, unittest.gs.framework.JsonDeserializeTest$Model#field4
    at com.alibaba.fastjson.parser.deserializer.FieldDeserializer.setValue(FieldDeserializer.java:162)
    at com.alibaba.fastjson.parser.deserializer.FieldDeserializer.setValue(FieldDeserializer.java:51)
    at com.alibaba.fastjson.parser.deserializer.ArrayListTypeFieldDeserializer.parseField(ArrayListTypeFieldDeserializer.java:56)
    at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.parseField(JavaBeanDeserializer.java:1241)
    at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:866)
    at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:288)
    at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:685)
    at com.alibaba.fastjson.JSON.parseObject(JSON.java:383)
    at com.alibaba.fastjson.JSON.parseObject(JSON.java:287)
    at com.alibaba.fastjson.JSON.parseObject(JSON.java:560)
    at unittest.gs.framework.JsonDeserializeTest.test5(JsonDeserializeTest.java:103)
    ... 24 more
Caused by: java.lang.NullPointerException
    at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:57)
    at java.base/jdk.internal.reflect.UnsafeQualifiedObjectFieldAccessorImpl.set(UnsafeQualifiedObjectFieldAccessorImpl.java:77)
    at java.base/java.lang.reflect.Field.set(Field.java:780)
    at com.alibaba.fastjson.parser.deserializer.FieldDeserializer.setValue(FieldDeserializer.java:157)
    ... 34 more
8

请问,出现bug的fastjson的版本是多少?可以提供一个可以复现这个bug的最小demo吗?能用maven和gradle建工程就最好了。

8

fastjson的版本是1.2.62,用该版本的fastjson执行我贴的代码即可。我使用的单元测试框架是testng,使用main方法执行也可以。

7

你好,bug已经被我修复并且被合并进master分支了。你可以用最新的master分支的代码去试一下你有问题的demo。@guyeu

0

额外请教一个问题,fastjson是如何将构造方法的若干个参数和json字符串中的若干个字段一一对应的呢,因为json是不保证顺序的,而运行时是没办法获取到参数的变量名的。 在这种情况下,给每个参数标注一个JSONField注解能否解决以上问题?

2

第一个问题,你可以去网上找讲述fastjson原理的文章去看一下,然后可以对照着阅读源码,而且不要忘记了无参的默认构造函数。 第二个问题,JSONField确实可以制定序列化字段的顺序,详情可以看一下下面这篇文章 https://juejin.im/post/5c1e02b45188257a937f9fea

5

额。。您这是回复错人了吗

2

比如说,有一个json

{
    "key1":1,
    "key2":2
}

对应的java类:

public class Model {
    public final int key1;
    public final int key2;
    public Model(int key1, int key2) {
        this.key1 = key1;
        this.key2 = key2;
    }
}

fastjson可以把上面的json反序列化为Model对象,但是我不是很清楚fastjson是如何保证json里的key1就是对象里的key1的,因为构造方法有可能是:

    public Model(int key2, int key1) {
        this.key1 = key1;
        this.key2 = key2;
    }
2

@guyeu 我以为你想问的是,第一fastjson的运行原理是什么?第二,在序列化时如何保证字段的序列化顺序。 至于你现在问的问题,你可以下载fastjson源码开启调试模式然后单步调试,如果不懂网上有很多讲述fastjson原理的文章,可以先看文章再去调试。 最后,你问的问题都是和issue无关的,这并不是一个很好的习惯。在issue被解决后应该及时关闭issue。

7

好吧。。issue的问题已经解决,thx。