fastjson2 icon indicating copy to clipboard operation
fastjson2 copied to clipboard

[BUG] 序列化枚举类如果是负数会直接crash

Open stc-W opened this issue 9 months ago • 2 comments

问题描述

简要描述您碰到的问题。 在序列化枚举类时有负数的情况

环境信息

请填写以下信息:

  • OS信息: [e.g.:Ubuntu 22.04.3 LTS]
  • JDK信息: [e.g.:openjdk 11.0.22 2024-01-16]
  • 版本信息:[e.g.:Fastjson 2.0.49 兼容1.x.x]

重现步骤

Test case

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.annotation.JSONField;
import org.junit.Test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

public class Test242 {

    @Test
    public void testParseObjectWithNegativeEnumValue() {
        String jsonStr = "{\"code\": -1}";
        Mock m = JSON.parseObject(jsonStr, new TypeReference<Mock>() {}, Feature.SupportAutoType);
        assertNotNull(m);
        assertEquals(EnumClass.NEGATIVE_ONE, m.getCode());
    }
}

class Mock {
    @JSONField(name = "code")
    private EnumClass code;

    public EnumClass getCode() {
        return code;
    }

    public void setCode(EnumClass code) {
        this.code = code;
    }
}

enum EnumClass {
    A(1),
    NEGATIVE_ONE(-1);

    private final int code;

    EnumClass(int code) {
        this.code = code;
    }

    public int getCode() {
        return code;
    }

    public static EnumClass valueOf(int code) {
        for (EnumClass enumClass : EnumClass.values()) {
            if (enumClass.getCode() == code) {
                return enumClass;
            }
        }
        return null;
    }
}
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import org.junit.Test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

public class Test243 {

    @Test
    public void testEnumDeserializationWithNegativeValue() {
        String jsonStr = "{\"code\":-1}"; // Updated JSON with a negative code value
        Mock m = JSON.parseObject(jsonStr, Mock.class, Feature.AllowISO8601DateFormat);
        assertNotNull(m);
        assertEquals(-1, m.getCode().getCode()); // Expected to get the code value -1
    }

    static class Mock {
        private EnumClass code;

        public EnumClass getCode() {
            return code;
        }

        public void setCode(EnumClass code) {
            this.code = code;
        }
    }

    enum EnumClass {
        A(1);

        private int code;

        EnumClass(int code) {
            this.code = code;
        }

        public int getCode() {
            return code;
        }

        public void setCode(int code) {
            this.code = code;
        }
    }
}
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import org.junit.Test;

import java.lang.reflect.Type;

public class Test244 {

    @Test
    public void testEnumDeserializationWithNegativeValue() {
        String jsonStr = "{\"code\":-1}";
        Type type = new TypeReference<Mock>(){}.getType();
        Mock m = JSON.parseObject(jsonStr, type);
        assert m != null;
        assert m.getCode().getCode() == -1;
    }
}

class Mock {
    private EnumClass code;

    public EnumClass getCode() {
        return code;
    }

    public void setCode(EnumClass code) {
        this.code = code;
    }
}

enum EnumClass {
    A(1),
    B(-1);

    private int code;

    EnumClass(int code) {
        this.code = code;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }
}
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.annotation.JSONField;
import org.junit.Test;
import java.nio.charset.StandardCharsets;
import java.nio.charset.CharsetDecoder;
import java.lang.reflect.Type;
import com.alibaba.fastjson.parser.Feature;

public class Test251 {
    @Test
    public void testParseObjectWithNegativeEnumValue() {
        byte[] jsonBytes = "{\"code\": -1}".getBytes(StandardCharsets.UTF_8);
        
        CharsetDecoder charsetDecoder = StandardCharsets.UTF_8.newDecoder();
        Type clazz = Mock.class;
        Feature[] features = new Feature[0];

        Mock m = JSON.parseObject(jsonBytes, 0, jsonBytes.length, charsetDecoder, clazz, features);
        assert m != null;
        assert m.getCode().getCode() == -1;
    }

    class Mock {
        @JSONField(name = "code")
        private EnumClass code;

        Mock() {}

        public EnumClass getCode() {
            return code;
        }

        public void setCode(EnumClass code) {
            this.code = code;
        }
    }

    enum EnumClass {
        A(1);

        private int code;

        EnumClass(int code) {
            this.code = code;
        }

        public int getCode() {
            return code;
        }

        public void setCode(int code) {
            this.code = code;
        }
    }
}

期待的正确结果

能够正常序列化

相关日志输出

*com.alibaba.fastjson.JSONException: read field 'Test243$Mock.setCode, offset 11, character }, line 1, column 11, fastjson-version 2.0.49 {"code":-1}

at com.alibaba.fastjson.JSON.parseObject(JSON.java:553)
at Test243.testEnumDeserializationWithNegativeValue(Test243.java:13)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
at java.base/java.lang.reflect.Method.invoke(Method.java:578)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:232)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55)

Caused by: com.alibaba.fastjson2.JSONException: No enum ordinal Test243.EnumClass.-1 at com.alibaba.fastjson2.reader.ObjectReaderImplEnum.readObject(ObjectReaderImplEnum.java:238) at com.alibaba.fastjson2.reader.FieldReaderObject.readFieldValue(FieldReaderObject.java:154) at com.alibaba.fastjson2.reader.ObjectReader1.readObject(ObjectReader1.java:286) at com.alibaba.fastjson.JSON.parseObject(JSON.java:543) ... 26 more。*

stc-W avatar May 07 '24 03:05 stc-W

源码由于用到了数组,所以判断了 index 不能为负数

https://github.com/alibaba/fastjson2/blob/c86bb8082c7ab2d5efbc04cb6063b6726d84c613/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderImplEnum.java#L235-L240

你可以自定义枚举的反序列化,代码如下:

        ParserConfig.getGlobalInstance().putDeserializer(EnumClass.class, new ObjectDeserializer() {

            @SuppressWarnings("unchecked")
            @Override
            public <T> T deserialze(final DefaultJSONParser parser, final Type type, final Object fieldName) {
                Integer intValue = parser.parseObject(int.class);
                if (intValue == -1) {
                    return (T) EnumClass.NEGATIVE_ONE;
                }

                return (T) Arrays.stream(EnumClass.values())
                        .filter(it -> it.getCode() == intValue)
                        .findFirst()
                        .orElseThrow(() -> new IllegalArgumentException("No enum value: " + intValue));
            }
        });

eahau avatar May 07 '24 15:05 eahau

https://oss.sonatype.org/content/repositories/snapshots/com/alibaba/fastjson/2.0.50-SNAPSHOT/ 问题已修复,请帮忙用2.0.50-SNAPSHOT版本在你本地验证下

wenshao avatar May 10 '24 00:05 wenshao

https://github.com/alibaba/fastjson2/releases/tag/2.0.50 2.0.50已发布,请用新版本

wenshao avatar May 12 '24 05:05 wenshao