Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
stc-W opened this issue May 7, 2024 · 3 comments
Closed

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

stc-W opened this issue May 7, 2024 · 3 comments
Labels
bug Something isn't working fixed
Milestone

Comments

@stc-W
Copy link

stc-W commented May 7, 2024

问题描述

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

环境信息

请填写以下信息:

  • 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 stc-W added the bug Something isn't working label May 7, 2024
@eahau
Copy link

eahau commented May 7, 2024

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

int intValue = jsonReader.readInt32Value();
if (valueField == null) {
if (intValue < 0 || intValue >= ordinalEnums.length) {
throw new JSONException("No enum ordinal " + enumClass.getCanonicalName() + "." + intValue);
}
fieldValue = ordinalEnums[intValue];

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

        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));
            }
        });

@wenshao
Copy link
Member

wenshao commented May 10, 2024

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

@wenshao wenshao added this to the 2.0.50 milestone May 10, 2024
@wenshao wenshao added the fixed label May 10, 2024
@wenshao
Copy link
Member

wenshao commented May 12, 2024

@wenshao wenshao closed this as completed May 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working fixed
Projects
None yet
Development

No branches or pull requests

3 participants