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] 设置 ValueFilter 后,枚举类型的 Map.key 输出会无故增加多余的双引号 #3293

Open
CodePlayer opened this issue Jan 17, 2025 · 0 comments
Labels
bug Something isn't working

Comments

@CodePlayer
Copy link
Contributor

CodePlayer commented Jan 17, 2025

问题描述

如以下代码所示,一个使用枚举值作为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 就没有这个问题

重现步骤

如何操作可以重现该问题:

public interface ValueEnum<V> {

	/**
	 * 获取枚举的 value 值
	 */
	V getValue();

}

public enum MemberType implements ValueEnum<Integer> {
	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<?> e) {
			if (value instanceof ValueEnum<?> t) {
				return t.getValue();
			}
			return e.ordinal();
		}
		return value;
	};
	Map<MemberType, Integer> 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() 的源代码,里面似乎只有双引号相关的判断:

} else if (ch == '"') {
if (offset + 1 < bytes.length
&& bytes[offset + 1] == '"'
) {
byte c0 = bytes[offset];
offset += 2;
if (c0 == '0' || c0 == 'N') {
val = false;
} else if (c0 == '1' || c0 == 'Y') {
val = true;
} else {
throw new JSONException("can not convert to boolean : " + c0);
}
} else {
String str = readString();
if ("true".equalsIgnoreCase(str)) {
return true;
}
if ("false".equalsIgnoreCase(str)) {
return false;
}
if (str.isEmpty() || "null".equalsIgnoreCase(str)) {
wasNull = true;
return false;
}
throw new JSONException("can not convert to boolean : " + str);
}
} else if (ch == '[') {

不过在 readInt32Value()readInt64Value() 等方法里面,又有单引号相关的判断。

此外,我自己也算是 Fastjson 的资深用户了,使用过程中也或多或少遇到过一些表现不一致的问题,有些可能是出于性能考虑设计如此,有些感觉就是部分逻辑判断太过分散导致的。

我举个例子:判断一个值是否为 null,这样的代码有好几十处,我在代码的不同地方看到了至少3种不同形式的判断:
形式一:

// 判断值是否为 null
str.isEmpty() || "null".equals(str)

形式二:

// 判断值是否为 null
str.isEmpty() || "null".equals(str) || "NULL".equals(str)

形式三:

str.isEmpty() || "null".equalsIgnoreCase(str)

判断一个值是否为 null,虽然代码只有一行,但因为许多地方的代码都是分开写的,这就导致判断是否为 null 的逻辑并不统一,导致输出的结果也可能多种多样。
而且,如果后续要统一调整或扩展也比较麻烦,一旦改漏一个地方,就可能导致意外发生。

我建议,将类似这样影响输出结果的逻辑判断,都尽量封装为统一的方法,比如可以在 TypeUtils 中定义如下方法:

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 的可能性也就相对比较高。

@CodePlayer CodePlayer added the bug Something isn't working label Jan 17, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

1 participant