-
Notifications
You must be signed in to change notification settings - Fork 544
Attempt to provide a built-in WriteNonStringValueAsString serializer … #3909
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
base: main
Are you sure you want to change the base?
Conversation
…for base types, for issue alibaba#3870
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR introduces built-in serializers for base types to support the WriteNonStringValueAsString feature, addressing issue #3870. The implementation provides convenience for users who want to globally register serializers that write numeric and boolean values as strings.
Key Changes:
- Adds a new
ObjectWriterAsStringclass with pre-defined serializer instances for primitive types, wrapper classes, BigInteger, BigDecimal, and primitive arrays - Updates
ObjectWriterCreator.getInitWriter()to recognize and use registeredObjectWriterAsStringinstances - Refines import statement in
ObjectWriterCreatorfrom wildcard to specific import
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterAsString.java | Introduces abstract class with 18 static serializer instances for converting base types to string representations during JSON serialization |
| core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterCreator.java | Adds logic to detect and return ObjectWriterAsString instances in the else block of getInitWriter method; refines filter import |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| } | ||
| } else { | ||
| ObjectWriter objectWriter = provider.cache.get(fieldClass); | ||
| if (objectWriter != null && objectWriter instanceof ObjectWriterAsString) { |
Copilot
AI
Dec 16, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The instanceof check should use pattern matching or avoid redundant null check. Since this code checks if objectWriter is not null implicitly through the cache.get() call, the instanceof check will return false for null values anyway, making the explicit null check redundant. Consider simplifying to just check the instanceof condition.
| if (objectWriter != null && objectWriter instanceof ObjectWriterAsString) { | |
| if (objectWriter instanceof ObjectWriterAsString) { |
| public abstract class ObjectWriterAsString implements ObjectWriter { | ||
| private ObjectWriterAsString() { | ||
| } |
Copilot
AI
Dec 16, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The class and its public static fields lack documentation. Since this is a public API that users will directly interact with to register custom serializers, comprehensive JavaDoc should be added to explain the purpose of this class, when to use it, and provide usage examples. Each public static field should also have documentation explaining what type it serializes.
| throw new JSONException("ObjectWriterAsString.write() should NOT be called directly: " | ||
| + object.getClass().getName()); |
Copilot
AI
Dec 16, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error message references the base class method but doesn't provide actionable guidance. Consider updating the message to explain that users should use the specific static instances (e.g., OF_INTEGER, OF_LONG) instead of calling the base write method directly, and provide an example of correct usage.
| throw new JSONException("ObjectWriterAsString.write() should NOT be called directly: " | |
| + object.getClass().getName()); | |
| throw new JSONException( | |
| "ObjectWriterAsString.write() should NOT be called directly for type: " | |
| + object.getClass().getName() | |
| + ".\n" | |
| + "Please use one of the specific static instances (e.g., ObjectWriterAsString.OF_INTEGER, ObjectWriterAsString.OF_LONG) instead.\n" | |
| + "Example: ObjectWriterAsString.OF_INTEGER.write(jsonWriter, value, fieldName, fieldType, features);" | |
| ); |
| public abstract class ObjectWriterAsString implements ObjectWriter { | ||
| private ObjectWriterAsString() { | ||
| } | ||
|
|
||
| public static final ObjectWriterAsString OF_INTEGER = new ObjectWriterAsString() { | ||
| @Override | ||
| public void write(JSONWriter jsonWriter, Object object, Object fieldName, Type fieldType, long features) { | ||
| if (object != null) { | ||
| jsonWriter.writeString(((Number) object).intValue()); | ||
| } else { | ||
| jsonWriter.writeNull(); | ||
| } | ||
| } | ||
| }; | ||
| public static final ObjectWriterAsString OF_INT_VALUE = OF_INTEGER; | ||
|
|
||
| public static final ObjectWriterAsString OF_LONG = new ObjectWriterAsString() { | ||
| @Override | ||
| public void write(JSONWriter jsonWriter, Object object, Object fieldName, Type fieldType, long features) { | ||
| if (object != null) { | ||
| jsonWriter.writeString(((Number) object).longValue()); | ||
| } else { | ||
| jsonWriter.writeNull(); | ||
| } | ||
| } | ||
| }; | ||
| public static final ObjectWriterAsString OF_LONG_VALUE = OF_LONG; | ||
|
|
||
| public static final ObjectWriterAsString OF_FLOAT = new ObjectWriterAsString() { | ||
| @Override | ||
| public void write(JSONWriter jsonWriter, Object object, Object fieldName, Type fieldType, long features) { | ||
| if (object != null) { | ||
| jsonWriter.writeString(((Number) object).floatValue()); | ||
| } else { | ||
| jsonWriter.writeNull(); | ||
| } | ||
| } | ||
| }; | ||
| public static final ObjectWriterAsString OF_FLOAT_VALUE = OF_FLOAT; | ||
|
|
||
| public static final ObjectWriterAsString OF_DOUBLE = new ObjectWriterAsString() { | ||
| @Override | ||
| public void write(JSONWriter jsonWriter, Object object, Object fieldName, Type fieldType, long features) { | ||
| if (object != null) { | ||
| jsonWriter.writeString(((Number) object).doubleValue()); | ||
| } else { | ||
| jsonWriter.writeNull(); | ||
| } | ||
| } | ||
| }; | ||
| public static final ObjectWriterAsString OF_DOUBLE_VALUE = OF_DOUBLE; | ||
|
|
||
| public static final ObjectWriterAsString OF_SHORT = new ObjectWriterAsString() { | ||
| @Override | ||
| public void write(JSONWriter jsonWriter, Object object, Object fieldName, Type fieldType, long features) { | ||
| if (object != null) { | ||
| jsonWriter.writeString(((Number) object).shortValue()); | ||
| } else { | ||
| jsonWriter.writeNull(); | ||
| } | ||
| } | ||
| }; | ||
| public static final ObjectWriterAsString OF_SHORT_VALUE = OF_SHORT; | ||
|
|
||
| public static final ObjectWriterAsString OF_BYTE = new ObjectWriterAsString() { | ||
| @Override | ||
| public void write(JSONWriter jsonWriter, Object object, Object fieldName, Type fieldType, long features) { | ||
| if (object != null) { | ||
| jsonWriter.writeString(((Number) object).byteValue()); | ||
| } else { | ||
| jsonWriter.writeNull(); | ||
| } | ||
| } | ||
| }; | ||
| public static final ObjectWriterAsString OF_BYTE_VALUE = OF_BYTE; | ||
|
|
||
| public static final ObjectWriterAsString OF_CHARACTER = new ObjectWriterAsString() { | ||
| @Override | ||
| public void write(JSONWriter jsonWriter, Object object, Object fieldName, Type fieldType, long features) { | ||
| if (object != null) { | ||
| jsonWriter.writeString(object.toString()); | ||
| } else { | ||
| jsonWriter.writeNull(); | ||
| } | ||
| } | ||
| }; | ||
| public static final ObjectWriterAsString OF_CHAR_VALUE = OF_CHARACTER; | ||
|
|
||
| public static final ObjectWriterAsString OF_BOOLEAN = new ObjectWriterAsString() { | ||
| @Override | ||
| public void write(JSONWriter jsonWriter, Object object, Object fieldName, Type fieldType, long features) { | ||
| if (object != null) { | ||
| jsonWriter.writeString(((Boolean) object).booleanValue()); | ||
| } else { | ||
| jsonWriter.writeNull(); | ||
| } | ||
| } | ||
| }; | ||
| public static final ObjectWriterAsString OF_BOOLEAN_VALUE = OF_BOOLEAN; | ||
|
|
||
| public static final ObjectWriterAsString OF_BIG_INTEGER = new ObjectWriterAsString() { | ||
| @Override | ||
| public void write(JSONWriter jsonWriter, Object object, Object fieldName, Type fieldType, long features) { | ||
| if (object != null) { | ||
| jsonWriter.writeBigInt((BigInteger) object, JSONWriter.Feature.WriteNonStringValueAsString.mask); | ||
| } else { | ||
| jsonWriter.writeNull(); | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| public static final ObjectWriterAsString OF_BIG_DECIMAL = new ObjectWriterAsString() { | ||
| @Override | ||
| public void write(JSONWriter jsonWriter, Object object, Object fieldName, Type fieldType, long features) { | ||
| if (object != null) { | ||
| jsonWriter.writeDecimal((BigDecimal) object, JSONWriter.Feature.WriteNonStringValueAsString.mask, null); | ||
| } else { | ||
| jsonWriter.writeNull(); | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| public static final ObjectWriterAsString OF_INT_ARRAY = new ObjectWriterAsString() { | ||
| @Override | ||
| public void write(JSONWriter jsonWriter, Object object, Object fieldName, Type fieldType, long features) { | ||
| if (object != null) { | ||
| jsonWriter.writeString((int[]) object); | ||
| } else { | ||
| jsonWriter.writeNull(); | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| public static final ObjectWriterAsString OF_LONG_ARRAY = new ObjectWriterAsString() { | ||
| @Override | ||
| public void write(JSONWriter jsonWriter, Object object, Object fieldName, Type fieldType, long features) { | ||
| if (object != null) { | ||
| jsonWriter.writeString((long[]) object); | ||
| } else { | ||
| jsonWriter.writeNull(); | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| public static final ObjectWriterAsString OF_FLOAT_ARRAY = new ObjectWriterAsString() { | ||
| @Override | ||
| public void write(JSONWriter jsonWriter, Object object, Object fieldName, Type fieldType, long features) { | ||
| if (object != null) { | ||
| jsonWriter.writeString((float[]) object); | ||
| } else { | ||
| jsonWriter.writeNull(); | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| public static final ObjectWriterAsString OF_DOUBLE_ARRAY = new ObjectWriterAsString() { | ||
| @Override | ||
| public void write(JSONWriter jsonWriter, Object object, Object fieldName, Type fieldType, long features) { | ||
| if (object != null) { | ||
| jsonWriter.writeString((double[]) object); | ||
| } else { | ||
| jsonWriter.writeNull(); | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| public static final ObjectWriterAsString OF_SHORT_ARRAY = new ObjectWriterAsString() { | ||
| @Override | ||
| public void write(JSONWriter jsonWriter, Object object, Object fieldName, Type fieldType, long features) { | ||
| if (object != null) { | ||
| jsonWriter.writeString((short[]) object); | ||
| } else { | ||
| jsonWriter.writeNull(); | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| public static final ObjectWriterAsString OF_BYTE_ARRAY = new ObjectWriterAsString() { | ||
| @Override | ||
| public void write(JSONWriter jsonWriter, Object object, Object fieldName, Type fieldType, long features) { | ||
| if (object != null) { | ||
| jsonWriter.writeString((byte[]) object); | ||
| } else { | ||
| jsonWriter.writeNull(); | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| public static final ObjectWriterAsString OF_BOOLEAN_ARRAY = new ObjectWriterAsString() { | ||
| @Override | ||
| public void write(JSONWriter jsonWriter, Object object, Object fieldName, Type fieldType, long features) { | ||
| if (object != null) { | ||
| jsonWriter.writeString((boolean[]) object); | ||
| } else { | ||
| jsonWriter.writeNull(); | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| @Override | ||
| public void write(JSONWriter jsonWriter, Object object, Object fieldName, Type fieldType, long features) { | ||
| if (object == null) { | ||
| jsonWriter.writeNull(); | ||
| return; | ||
| } | ||
|
|
||
| throw new JSONException("ObjectWriterAsString.write() should NOT be called directly: " | ||
| + object.getClass().getName()); | ||
| } | ||
| } |
Copilot
AI
Dec 16, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new ObjectWriterAsString class lacks test coverage. Since this is a new public API that provides built-in serializers for base types, tests should be added to verify that each serializer instance correctly serializes values as strings, handles null values properly, and integrates correctly when registered with ObjectWriterProvider. Consider adding tests that cover scenarios like registering these writers globally and verifying their usage through the ObjectWriterCreator.getInitWriter method.
|
建议在BigDecimal/BigInteger这两个类型的Writer实现中支持WriteNonStringValueAsString,这样改动小,用户使用也更方便 |
这两个序列化器目前是支持 WriteNonStringValueAsString 的: 用户意思想 ”一条配置,全局BigInteger/BigDecimal都序列化为字符串“,类似的问题#3563、#891也提到过(全局配置BooleanAsString不生效) |
…for base types, for issue #3870
What this PR does / why we need it?
是否可以提供如下功能:
对基础类型提供内置 NonStringValueAsString 序列化器;
方便用户全局注册。
尝试了一种实现方式(草稿,感觉不太好,无法避免装箱拆箱),跑了 BenchMark 程序和结果如下:
点击查看 benchmark 代码
jdk21-asm(fastjson2.creator=asm):



jdk17-asm:
jdk8-asm: