fix: Map subclass loses @type when serialized with ValueFilter (#3984)#4029
fix: Map subclass loses @type when serialized with ValueFilter (#3984)#4029
Conversation
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>
There was a problem hiding this comment.
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
writeTypeInfohandling toObjectWriterImplMap.writeWithFilter()so@typeis written when required. - Add JUnit tests covering
WriteClassNamewith/withoutValueFilter, 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.
| boolean writeTypeInfo = (fieldType == this.objectType && jsonWriter.isWriteMapTypeInfo(object, this.objectClass, features)) | ||
| || jsonWriter.isWriteTypeInfo(object, fieldType, features); | ||
| if (writeTypeInfo) { | ||
| writeTypeInfo(jsonWriter); | ||
| } | ||
|
|
||
| features |= jsonWriter.getFeatures(); |
| 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); |
| 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>
There was a problem hiding this comment.
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
@typeemission logic toObjectWriterImplMap.writeWithFilter()to matchwrite()behavior. - Add regression tests covering filtered/unfiltered and nested
Map-subclass serialization withWriteClassName.
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.
| 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(); |
| 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); |
| 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>
What this PR does / why we need it?
Fixes #3984
当使用
WriteClassName特性序列化HashMap子类时,如果同时设置了ValueFilter,@type字段会被静默丢弃,导致反序列化时丢失类型信息。复现代码: