Skip to content

fix: Map subclass loses @type when serialized with ValueFilter (#3984)#4029

Merged
wenshao merged 3 commits intomainfrom
fix/3984-map-writeClassName-with-filter
Mar 15, 2026
Merged

fix: Map subclass loses @type when serialized with ValueFilter (#3984)#4029
wenshao merged 3 commits intomainfrom
fix/3984-map-writeClassName-with-filter

Conversation

@wenshao
Copy link
Copy Markdown
Member

@wenshao wenshao commented Mar 15, 2026

What this PR does / why we need it?

Fixes #3984

当使用 WriteClassName 特性序列化 HashMap 子类时,如果同时设置了 ValueFilter@type 字段会被静默丢弃,导致反序列化时丢失类型信息。

复现代码:

CustomMap map = new CustomMap(); // extends HashMap
map.put("key1", "value1");
ValueFilter filter = (object, name, value) -> value;
String json = JSON.toJSONString(map, new ValueFilter[]{filter}, JSONWriter.Feature.WriteClassName);
// 实际输出: {"key1":"value1"}  — 缺少 @type
// 期望输出: {"@type":"...CustomMap","key1":"value1"}

Summary of your change

根因ObjectWriterImplMap.write() 方法在写入 Map 条目前会调用 writeTypeInfo(), writeWithFilter() 方法完全缺失了这一逻辑当存在 Filter write() 会委托给 writeWithFilter(),导致 @type 丢失修复 writeWithFilter()  startObject() 之后写入 Map 条目之前添加与 write() 一致的 writeTypeInfo 判断和调用逻辑boolean writeTypeInfo = (fieldType == this.objectType
        && jsonWriter.isWriteMapTypeInfo(object, this.objectClass, features))
        || jsonWriter.isWriteTypeInfo(object, fieldType, features);
if (writeTypeInfo) {
    writeTypeInfo(jsonWriter);
}

测试覆盖:
- 单层 Map 子类 + ValueFilter + WriteClassName
-  Filter 时行为不变对照)
- 嵌套 Map 子类验证内外层均写入 @type

Please indicate you've done the following:

- Made sure tests are passing and test coverage is added if needed.
- Made sure commit message follow the rule of https://www.conventionalcommits.org/.
- Considered the docs impact and opened a new docs issue or PR with docs changes if needed.

ObjectWriterImplMap.writeWithFilter() was missing the writeTypeInfo
call that write() had, causing @type to be silently dropped when
any filter was present with WriteClassName enabled.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@wenshao wenshao requested a review from Copilot March 15, 2026 00:44
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes missing @type emission when serializing Map subclasses with ValueFilter and WriteClassName enabled, and adds regression tests for the behavior.

Changes:

  • Add writeTypeInfo handling to ObjectWriterImplMap.writeWithFilter() so @type is written when required.
  • Add JUnit tests covering WriteClassName with/without ValueFilter, including nested map scenarios.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplMap.java Restores type info writing in the filtered map writer path.
core/src/test/java/com/alibaba/fastjson2/issues_3900/Issue3984.java Adds regression tests ensuring @type is preserved with filters and nested maps.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +620 to 626
boolean writeTypeInfo = (fieldType == this.objectType && jsonWriter.isWriteMapTypeInfo(object, this.objectClass, features))
|| jsonWriter.isWriteTypeInfo(object, fieldType, features);
if (writeTypeInfo) {
writeTypeInfo(jsonWriter);
}

features |= jsonWriter.getFeatures();
Comment on lines +27 to +28
assertTrue(json.contains("@type"), "JSON should contain @type when WriteClassName is enabled with ValueFilter: " + json);
assertTrue(json.contains("CustomMap"), "JSON should contain class name: " + json);
Comment on lines +58 to +61
int firstIdx = json.indexOf("@type");
int lastIdx = json.lastIndexOf("@type");
assertTrue(firstIdx >= 0, "Should contain @type: " + json);
assertTrue(firstIdx != lastIdx, "Should contain @type for both outer and inner maps: " + json);
Address review feedback: assert against full qualified type name
via CustomMap.class.getName() and count exact @type occurrences
instead of substring checks.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@wenshao wenshao requested a review from Copilot March 15, 2026 01:05
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a regression where @type metadata is omitted when serializing HashMap subclasses with WriteClassName if a ValueFilter is also applied (Issue #3984).

Changes:

  • Add @type emission logic to ObjectWriterImplMap.writeWithFilter() to match write() behavior.
  • Add regression tests covering filtered/unfiltered and nested Map-subclass serialization with WriteClassName.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplMap.java Ensures type info is written even when filters are present.
core/src/test/java/com/alibaba/fastjson2/issues_3900/Issue3984.java Adds regression coverage for @type output with ValueFilter and nested map subclasses.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 617 to 626
jsonWriter.startObject();
Map map = (Map) object;

boolean writeTypeInfo = (fieldType == this.objectType && jsonWriter.isWriteMapTypeInfo(object, this.objectClass, features))
|| jsonWriter.isWriteTypeInfo(object, fieldType, features);
if (writeTypeInfo) {
writeTypeInfo(jsonWriter);
}

features |= jsonWriter.getFeatures();
Comment on lines +24 to +30
String json = JSON.toJSONString(map,
new ValueFilter[]{valueFilter},
JSONWriter.Feature.WriteClassName);

String expectedType = "\"@type\":\"" + CustomMap.class.getName() + "\"";
assertTrue(json.contains(expectedType),
"JSON should contain " + expectedType + ", actual: " + json);
Comment on lines +56 to +68
String json = JSON.toJSONString(outer,
new ValueFilter[]{valueFilter},
JSONWriter.Feature.WriteClassName);

String typeToken = "\"@type\":\"" + CustomMap.class.getName() + "\"";
int count = 0;
int idx = 0;
while ((idx = json.indexOf(typeToken, idx)) != -1) {
count++;
idx += typeToken.length();
}
assertEquals(2, count,
"Should contain exactly 2 @type entries for outer and inner maps: " + json);
Parse serialized JSON and assert @type via JSONObject.getString()
rather than substring matching, making tests resilient to
formatting/order changes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@wenshao wenshao merged commit 14ea6ea into main Mar 15, 2026
62 of 63 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG]Map子类的WriteClassName加了ValueFilter后被漏掉了

2 participants