[BUG] 设置 ValueFilter 后,枚举类型的 Map.key 输出会无故增加多余的双引号
- Dominant language
- Java
- Stars
- 4.4k
- Forks
- 613
- Avg merge
- 1d 22h
- Merged PRs (30d)
- 6
Description
### 问题描述
如以下代码所示,一个使用枚举值作为Key的Map集合,使用 Fastjson 2.0.54 进行两次JSON输出(具体输出内容参见最后两行代码的注释):
1. 第一次没有设置 `valueFilter` ,输出就是正常的。
2. 第二次只是多设置了一个 `valueFilter`,输出的 Map.key 就会额外多带一个双引号。
### 环境信息
*请填写以下信息:*
- OS信息: Windows 11 64bit
- JDK信息: OpenJDK 8、17
- 版本信息:Fastjson2 2.0.54( **2.0.53 就没有这个问题** )
### 重现步骤
*如何操作可以重现该问题:*
```java
public interface ValueEnum {
/** 获取枚举的 value 值 */
V getValue();
}
public enum MemberType implements ValueEnum {
USER(1),
ADMIN(2),
AGENT(3);
final Integer value;
MemberType(Integer value) {
this.value = value;
}
@Override
public Integer getValue() {
return value;
}
}
@Test
public void test() {
final ValueFilter valueFilter = (object, name, value) -> {
if (value instanceof Enum) {
if (value instanceof ValueEnum) {
return ((ValueEnum) value).getValue();
}
return ((Enum) value).ordinal();
}
return value;
};
Map memberCountMap = new HashMap<>(4);
memberCountMap.put(MemberType.ADMIN, 1);
memberCountMap.put(MemberType.USER, 123);
System.out.println(JSON.toJSONString(memberCountMap)); // {"USER":123,"ADMIN":1}
System.out.println(JSON.toJSONString(memberCountMap, valueFilter)); // {"\"USER\"":123,"\"ADMIN\"":1}
}
```
#### 附加信息
【**顺便探讨一下**】
前两天,我看到有人反馈了一个 BUG #3283 , 说是无法解析单引号形式的 `'true'` 为 `boolean` 类型。
我顺便看了一下 `readBoolValue()` 的源代码,里面似乎只有双引号相关的判断:
https://github.com/alibaba/fastjson2/blob/714a2b6ff235709d4568cf4fb88e41ee767f11eb/core/src/main/java/com/alibaba/fastjson2/JSONReaderUTF8.java#L7124-L7153
不过在 `readInt32Value()` 和 `readInt64Value()` 等方法里面,又有单引号相关的判断。
此外,我自己也算是 Fastjson 的资深用户了,使用过程中也或多或少遇到过一些表现不一致的问题,有些可能是出于性能考虑设计如此,有些感觉就是部分逻辑判断太过分散导致的。
我举个例子:判断一个值是否为 `null`,这样的代码有好几十处,我在代码的不同地方看到了至少3种不同形式的判断:
形式一:
```java
// 判断值是否为 null
str.isEmpty() || "null".equals(str)
```
形式二:
```java
// 判断值是否为 null
str.isEmpty() || "null".equals(str) || "NULL".equals(str)
```
形式三:
```java
str.isEmpty() || "null".equalsIgnoreCase(str)
```
判断一个值是否为 `null`,虽然代码只有一行,但因为许多地方的代码都是分开写的,这就导致判断是否为 `null` 的逻辑**并不统一**,导致输出的结果也可能多种多样。
而且,如果后续要**统一调整或扩展**也比较麻烦,一旦改漏一个地方,就可能导致意外发生。
我建议,将类似这样影响输出结果的逻辑判断,都尽量封装为统一的方法,比如可以在` TypeUtils` 中定义如下方法:
```java
public static boolean considerAsNull(@Nullable String str) {
return str == null || str.isEmpty() || "null".equals(str) || "NULL".equals(str);
}
```
1. 上面所有涉及到值为 `null` 的判断都统一调用该方法。
2. 如果确有特殊情况,可以另起一个方法,并用注释进行特别说明。
我在本地已经在尝试这样去重构,但是,多种代码的逻辑不一样,如果要统一的话,我不清楚你最终会选择哪一种。
有些疑似 bug 的我也不太清楚是代码疏漏,还是有特殊考虑、设计如此,也需要确认,所以也不好擅自下手。
此外,有些代码,通过代码分析工具也能够发现一些问题,比如分支条件必定为 `true/false`、分支条件不可达、变量定义了未使用、代码重复。
这种地方,潜藏 bug 的可能性也就相对比较高。
Contributor guide
Research direction
Run the provided JSON.toJSONString reproducer with and without ValueFilter, then trace the Map key serialization path and ValueFilter handling. Add a regression test for enum keys implementing ValueEnum; done means filtered output contains USER and ADMIN as normal keys without extra escaped double quotes.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100