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]反序列化空EnumSet可能导致异常 #2423

Closed
superinkfish opened this issue Apr 10, 2024 · 3 comments
Closed

[BUG]反序列化空EnumSet可能导致异常 #2423

superinkfish opened this issue Apr 10, 2024 · 3 comments
Assignees
Labels
bug Something isn't working fixed
Milestone

Comments

@superinkfish
Copy link

superinkfish commented Apr 10, 2024

问题描述

简要描述您碰到的问题。
一个含有EnumSet的空集合,在JSON序列化后,再用FastJSON2反序列化,会报异常。

环境信息

  • OS信息: Windows 11 & CentOS 7u2
  • JDK信息: Openjdk 1.8.0
  • 版本信息:fastjson2=2.0.42

重现步骤

  1. 复现源码:
import java.util.EnumSet;
import java.util.concurrent.TimeUnit;

public class TestFastJson {

    public static void main(String[] args) {
        Bean bean = new Bean();
        System.out.println("Source: \n" + bean);

        String jsonStr = com.alibaba.fastjson.JSON.toJSONString(bean);
        System.out.println("\nFastJSON:\n" + jsonStr);
        System.out.println(com.alibaba.fastjson.JSON.parseObject(jsonStr, Bean.class));

        jsonStr = com.alibaba.fastjson2.JSON.toJSONString(bean);
        System.out.println("\nFastJSON2:\n" + jsonStr);
        System.out.println(com.alibaba.fastjson2.JSON.parseObject(jsonStr, Bean.class));
    }
}

class Bean {
    private EnumSet<TimeUnit> unit = EnumSet.noneOf(TimeUnit.class);

    public EnumSet<TimeUnit> getUnit() {
        return unit;
    }

    public void setUnit(EnumSet<TimeUnit> unit) {
        this.unit = unit;
    }

    @Override
    public String toString() {
        return "Bean [unit=" + unit + "]";
    }
}

在使用FastJSON时运行正常,FastJSON2反序列化时抛出异常:

Source: 
Bean [unit=[]]

FastJSON:
{"unit":[]}
Bean [unit=[]]

FastJSON2:
{"unit":[]}
Exception in thread "main" java.lang.IllegalArgumentException: Collection is empty
	at java.util.EnumSet.copyOf(EnumSet.java:174)
	at com.alibaba.fastjson2.reader.ObjectReaderImplList.lambda$of$0(ObjectReaderImplList.java:98)
	at com.alibaba.fastjson2.reader.ObjectReaderImplList.readObject(ObjectReaderImplList.java:626)
	at com.alibaba.fastjson2.reader.ORG_1_1_Bean.readObject(Unknown Source)
	at com.alibaba.fastjson2.JSON.parseObject(JSON.java:726)
	at TestFastJson.main(TestFastJson.java:16)

期待的正确结果

期望反序列化后能生成和原始bean相等的对象。

附加信息

  • 问题原因:
    com.alibaba.fastjson2.reader.ObjectReaderImplList.of(Type, Class, long)中,第96~98行有代码:
        } else if (listClass == EnumSet.class) {
            instanceClass = HashSet.class;
            builder = (o) -> EnumSet.copyOf((Collection) o);

这里的EnumSet.copyOf(Collection<E> c)的c隐含的是Nonnull,而空EnumSet的o为空集合,因此抛异常。
修复,把96~98行改为如下代码:

        } else if (listClass == EnumSet.class) {
            instanceClass = HashSet.class;
            final Class<Enum> clazz;
            try {
                clazz = (Class<Enum>)Class.forName(itemType.getTypeName());
            } catch (ClassNotFoundException e) {
                throw new IllegalStateException(e.getMessage(), e);
            }
            builder = (o) -> {
                if (((Collection<?>)o).isEmpty()) {
                    return EnumSet.noneOf(clazz);
                }
                return EnumSet.copyOf((Collection)o);
            };

再次执行,问题消除。

Source: 
Bean [unit=[]]

FastJSON:
{"unit":[]}
Bean [unit=[]]

FastJSON2:
{"unit":[]}
Bean [unit=[]]
@superinkfish superinkfish added the bug Something isn't working label Apr 10, 2024
@wenshao wenshao added this to the 2.0.49 milestone Apr 10, 2024
@superinkfish
Copy link
Author

在最新版的fastjson2 2.0.48中验证问题也存在。
同样,如果把EnumSet换成EnumMap,问题一样存在。
源码:

import java.util.EnumMap;
import java.util.concurrent.TimeUnit;

public class TestFastJson {

    public static void main(String[] args) {
        Bean bean = new Bean();
        System.out.println("Source: \n" + bean);

        String jsonStr = com.alibaba.fastjson.JSON.toJSONString(bean);
        System.out.println("\nFastJSON:\n" + jsonStr);
        System.out.println(com.alibaba.fastjson.JSON.parseObject(jsonStr, Bean.class));

        jsonStr = com.alibaba.fastjson2.JSON.toJSONString(bean);
        System.out.println("\nFastJSON2:\n" + jsonStr);
        System.out.println(com.alibaba.fastjson2.JSON.parseObject(jsonStr, Bean.class));
    }
}

class Bean {
    private EnumMap<TimeUnit, Integer> map = new EnumMap<>(TimeUnit.class);

    public EnumMap<TimeUnit, Integer> getMap() {
        return map;
    }

    public void setMap(EnumMap<TimeUnit, Integer> map) {
        this.map = map;
    }

    @Override
    public String toString() {
        return "Bean [map=" + map + "]";
    }
}

运行结果:

Source: 
Bean [map={}]

FastJSON:
{"map":{}}
Bean [map={}]

FastJSON2:
{"map":{}}
Exception in thread "main" com.alibaba.fastjson2.JSONException: create map error
	at com.alibaba.fastjson2.reader.ObjectReaderImplMapTyped.createInstance(ObjectReaderImplMapTyped.java:154)
	at com.alibaba.fastjson2.reader.ObjectReaderImplMapTyped.readObject(ObjectReaderImplMapTyped.java:308)
	at com.alibaba.fastjson2.reader.ORG_1_1_Bean.readObject(Unknown Source)
	at com.alibaba.fastjson2.JSON.parseObject(JSON.java:786)
	at TestFastJson.main(TestFastJson.java:16)
Caused by: java.lang.InstantiationException: java.util.EnumMap
	at java.lang.Class.newInstance(Class.java:427)
	at com.alibaba.fastjson2.reader.ObjectReaderImplMapTyped.createInstance(ObjectReaderImplMapTyped.java:152)
	... 4 more
Caused by: java.lang.NoSuchMethodException: java.util.EnumMap.<init>()
	at java.lang.Class.getConstructor0(Class.java:3082)
	at java.lang.Class.newInstance(Class.java:412)
	... 5 more

问题在com.alibaba.fastjson2.reader.ObjectReaderImplMapTyped.createInstance(long)中的instanceType.newInstance()EnumMap没有无参构造器。
若在fastjson2 2.0.48的com.alibaba.fastjson2.reader.ObjectReaderImplMapTyped.readObject(JSONReader, Type, Object, long)第307行} else {前加入:

        } else if (EnumMap.class.isAssignableFrom(instanceType)) {
            try {
                object = EnumMap.class.getConstructor(Class.class).newInstance(Class.forName(keyType.getTypeName()));
            } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
                | InvocationTargetException | NoSuchMethodException | SecurityException | ClassNotFoundException e) {
                throw new IllegalStateException(e.getMessage(), e);
            }

则测试代码能正常运行通过。

@wenshao
Copy link
Member

wenshao commented Apr 12, 2024

https://oss.sonatype.org/content/repositories/snapshots/com/alibaba/fastjson2/fastjson2/2.0.49-SNAPSHOT/
问题已经修复,请帮忙用2.0.49-SNAPSHOT版本验证,2.0.49版本预计在本周末(4月14日)前发布

@wenshao
Copy link
Member

wenshao commented Apr 14, 2024

@wenshao wenshao closed this as completed Apr 14, 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