fastjson2 icon indicating copy to clipboard operation
fastjson2 copied to clipboard

[FEATURE] 当对象中含有BigInteger类型的字段时,没有办法在全局设置中强制转换String

Open moliu opened this issue 1 month ago • 9 comments

@Data
public static class test {
        int a = 1;
        Integer b = 2;
        long c = 1;
        Long d = 1123123213213123213L;

        //@JSONField(serializeUsing = BigIntegerToStringSerializer.class)
        BigInteger e = new BigInteger("123");

    }

public static void main(String[] args) {
        FastJsonConfig jsonConfig = new FastJsonConfig();
        jsonConfig.setWriterFeatures();

        ObjectWriterProvider objectWriterProvider=new ObjectWriterProvider();
        objectWriterProvider.register(Long.class, ObjectWriters.ofToString(Object::toString));
        JSONWriter.Context context= JSONFactory.createWriteContext(objectWriterProvider);

        JSON.config(JSONWriter.Feature.WriteLongAsString);

        //JSON.config(JSONWriter.Feature.WriteNonStringValueAsString);
        //JSON.config(JSONWriter.Feature.BrowserCompatible);

        JSON.register(Long.class, ObjectWriters.ofToString(Object::toString));
        JSON.register(Long.TYPE, ObjectWriters.ofToString(Object::toString));
        JSON.register(BigInteger.class, ObjectWriters.ofToString(Object::toString));


        String json1 = JSONObject.toJSONString(new test());


        log.info(json1);
    }

输出结果{"a":1,"b":2,"c":"1","d":"1123123213213123213","e":123}

JSONWriter.Feature.WriteNonStringValueAsString和JSONWriter.Feature.BrowserCompatible虽然能部分实现,但是会引入更多的问题,一个会强制把所有的对象都转为字符串,另一个则是为了适配浏览器兼容只会把超过9007199254740991和小于-9007199254740991的转为string,而且空值还会按 JSON 标准输出 null/undefined

看了下源码,序列化时最终会进入JSONWriterUTF16.writeBigInt方法,其中有个isWriteAsString来判断是否转为String

protected static boolean isWriteAsString(BigDecimal value, long features) { return (features & MASK_WRITE_NON_STRING_VALUE_AS_STRING) != 0 || ((features & MASK_BROWSER_COMPATIBLE) != 0 && !isJavaScriptSupport(value)); }

只有JSONWriter.Feature.WriteNonStringValueAsString或者JSONWriter.Feature.BrowserCompatible并且超出了9007199254740991范围才会转String

希望能增加一个JSONWriter.Feature.WriteBigIntegerAsString的枚举和判断条件,或者有其他的方法请告知一下,不胜感激

moliu avatar Nov 13 '25 16:11 moliu

我查了下源码,全局注册字段级别序列化器,对于Java内置类型,仅支持下面几种(不清楚是否为 bug):

    protected ObjectWriter getInitWriter(ObjectWriterProvider provider, Class fieldClass) {
        if (fieldClass == Date.class) {
            if ((provider.userDefineMask & ObjectWriterProvider.TYPE_DATE_MASK) != 0) {
                ObjectWriter objectWriter = provider.cache.get(fieldClass);
                if (objectWriter != ObjectWriterImplDate.INSTANCE) {
                    return objectWriter;
                }
            }
        } else if (fieldClass == int.class || fieldClass == Integer.class) {
            if ((provider.userDefineMask & ObjectWriterProvider.TYPE_INT32_MASK) != 0) {
                ObjectWriter objectWriter = provider.cache.get(Integer.class);
                if (objectWriter != ObjectWriterImplInt32.INSTANCE) {
                    return objectWriter;
                }
            }
        } else if (fieldClass == long.class || fieldClass == Long.class) {
            if ((provider.userDefineMask & ObjectWriterProvider.TYPE_INT64_MASK) != 0) {
                ObjectWriter objectWriter = provider.cache.get(Long.class);
                if (objectWriter != ObjectWriterImplInt64.INSTANCE) {
                    return objectWriter;
                }
            }
        } else if (fieldClass == BigDecimal.class) {
            if ((provider.userDefineMask & ObjectWriterProvider.TYPE_DECIMAL_MASK) != 0) {
                ObjectWriter objectWriter = provider.cache.get(fieldClass);
                if (objectWriter != ObjectWriterImplBigDecimal.INSTANCE) {
                    return objectWriter;
                }
            }
        } else if (Enum.class.isAssignableFrom(fieldClass)) {
            ObjectWriter objectWriter = provider.cache.get(fieldClass);
            if (!(objectWriter instanceof ObjectWriterImplEnum)) {
                return objectWriter;
            }
        }
        return null;
    }

jujn avatar Nov 14 '25 08:11 jujn

是的,fastjson是可以的,这次搞升级太难了,目前看实现两个地方最好都能加一下

moliu avatar Nov 14 '25 08:11 moliu

增加biginteger的支持或者有别的方法麻烦提醒我一下,这个情况目前是完全没法绕过了,感谢

moliu avatar Nov 14 '25 09:11 moliu

感谢

moliu avatar Nov 21 '25 01:11 moliu

请问这个功能大概什么时候能发到下个版本呢

moliu avatar Nov 21 '25 02:11 moliu

另外能否增加一个JSONWriter.Feature.WriteBigIntegerAsString,来实现局部的序列化设置

moliu avatar Nov 21 '25 06:11 moliu

您可以暂时用这种方式替代吗?(为每个 JavaBean 的 BigInteger 属性手动指定序列化器)

    public class TestData {
        @JSONField(serializeUsing = XxxWriter.class)
        BigInteger b = new BigInteger("123");
    }

jujn avatar Nov 21 '25 09:11 jujn

可以的,希望后续版本可以加上,这个场景目前不多

moliu avatar Nov 24 '25 02:11 moliu

同样需要增加一个:JSONWriter.Feature.WriteBigDecimalAsString

xzxiaoshan avatar Nov 29 '25 02:11 xzxiaoshan

另外能不能引入一个像是fastjson中的SerializeConfig,这样自由度更高吧,可以在局部序列化中自由配置,为什么把这个给去掉了呢。。。

moliu avatar Dec 12 '25 09:12 moliu

另外能不能引入一个像是fastjson中的SerializeConfig,这样自由度更高吧,可以在局部序列化中自由配置,为什么把这个给去掉了呢。。。

需要配置什么呢?2.x中的替代方案应该是 ObjectWriterProvider:

Image

jujn avatar Dec 12 '25 09:12 jujn

在消息转换器中,FastJsonHttpMessageConverter怎么使用ObjectWriterProvider呢

moliu avatar Dec 15 '25 01:12 moliu

在消息转换器中,FastJsonHttpMessageConverter怎么使用ObjectWriterProvider呢

可以通过JSONWriter.Context对象传入配置吧:

JSONWriter.Context context = JSONFactory.createWriteContext();
context.config(xxx);

jujn avatar Dec 15 '25 01:12 jujn

context.config只能使用固定的那些Feature,没法自定义,目前主要就是消息转换器这里的问题,1里面是可以在SerializeConfig里面配置Serializer的

moliu avatar Dec 15 '25 01:12 moliu

直接全局注册 JSONFactory.register(class, serializer) 可以吗

jujn avatar Dec 15 '25 01:12 jujn

另外我刚才这样试过了也是不行的 FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter(); FastJsonConfig fastJsonConfig = new FastJsonConfig();

    fastJsonConfig.setWriterFeatures(
            JSONWriter.Feature.PrettyFormat,
            JSONWriter.Feature.WriteMapNullValue
    );
    ObjectWriterProvider provider = fastJsonConfig.writerContext().getProvider();
    provider.register(BigInteger.class, ObjectWriters.ofToString(Object::toString));
    provider.register(Long.TYPE, ObjectWriters.ofToString(Object::toString));
    provider.register(Long.class, ObjectWriters.ofToString(Object::toString));
    fastJsonHttpMessageConverter.setFastJsonConfig(fastJsonConfig);

    System.out.println(JSON.toJSONString(new test(), fastJsonConfig.writerContext()));

moliu avatar Dec 15 '25 01:12 moliu

JSONFactory.getDefaultObjectWriterProvider().register(BigInteger.class, ObjectWriters.ofToString(Object::toString));也不行 JSONFactory.register(class, serializer)没这个方法啊

moliu avatar Dec 15 '25 01:12 moliu

JSON.register(class, serializer)(抱歉,我上面打错成 JSONFactory 了)

jujn avatar Dec 15 '25 02:12 jujn

JSON.register(BigInteger.class, ObjectWriters.ofToString(Object::toString)); 这个试过,2.0.60还不行,应该要等你的提交发到下个版本才行

moliu avatar Dec 15 '25 02:12 moliu

对了,看下这个是不是也是和上面一个问题,BigInteger没有按配置序列化

@Data
public static class test1 {
    int a = 1;
    Integer b = 2;
    long c = 1;
    Long d = 1123123213213123213L;

    //@JSONField(serializeUsing = BigIntegerToStringSerializer.class)
    BigInteger e = new BigInteger("123");

}


    JSONWriter.Context context = new JSONWriter.Context();
    ObjectWriterProvider provider = context.getProvider();
    provider.register(BigInteger.class, ObjectWriters.ofToString(Object::toString));
    provider.register(Long.TYPE, ObjectWriters.ofToString(Object::toString));
    provider.register(Long.class, ObjectWriters.ofToString(Object::toString));
    System.out.println(JSON.toJSONString(new test1(), context));

返回{"a":1,"b":2,"c":"1","d":"1123123213213123213","e":123}

moliu avatar Dec 15 '25 06:12 moliu

问题是没有内置的能力可以支持BigInteger转String啊。。。有的话我也不会这么写。。。

moliu avatar Dec 15 '25 08:12 moliu

我去想一下应该怎么实现,争取周三前给您答复

jujn avatar Dec 15 '25 08:12 jujn

非常感谢,另外上面说的有点多,其实就两个需求,一个是希望能增加JSONWriter.Feature.WriteBigIntegerAsString,或者JSONWriter.Feature.WriteLongAsString同样能支持BigInteger,这个是局部的配置,另一个是provider.register(BigInteger.class, ObjectWriters.ofToString(Object::toString))不生效,我看你的提交已经实现了

moliu avatar Dec 15 '25 08:12 moliu

我倾向于WriteNonStringValueAsString能够在BigInteger和BigDecimal这两个类型起作用,而不是引入这么大的改动

wenshao avatar Dec 20 '25 13:12 wenshao

我倾向于WriteNonStringValueAsString能够在BigInteger和BigDecimal这两个类型起作用,而不是引入这么大的改动


WriteNonStringValueAsString 这个名词是否范围有点大了。

最小化改动,是否应该是:参照 WriteLongAsString 新增 WriteBigIntegerAsString 和 WriteBigDecimalAsString 。

xzxiaoshan avatar Dec 22 '25 01:12 xzxiaoshan

那就把 WriteBigIntegerAsString 、 WriteBigDecimalAsString 、WriteNonStringValueAsString都加上,叫开发者自选,另外就是我看这个fix被关掉了,https://github.com/alibaba/fastjson2/pull/3876 ObjectWriterProvider provider = context.getProvider(); provider.register(BigInteger.class, ObjectWriters.ofToString(Object::toString));这个是不是还是不行呢

moliu avatar Dec 22 '25 01:12 moliu

BigInteger value = BigInteger.valueOf(123456789);
JSON.config(JSONWriter.Feature.WriteNonStringValueAsString);
String str = JSON.toJSONString(value);
System.out.println(str);
BigInteger value = BigInteger.valueOf(123456789);
JSON.register(BigInteger.class, new ObjectWriter<BigInteger>() {
    @Override
    public void write(JSONWriter jsonWriter, Object object, Object fieldName, Type fieldType, long features) {
        jsonWriter.writeBigInt((BigInteger) object, JSONWriter.Feature.WriteNonStringValueAsString.mask);
    }
});
String str = JSON.toJSONString(value);
System.out.println(str);

上面这两种使用方式能够满足你的需求么?

wenshao avatar Dec 22 '25 02:12 wenshao

第一种不行,因为我是序列化整个对象,而不是只序列化BigInteger,这样会把所有的属性都转为String,我需要的是一个只转换BigInteger,所以我需要的是有一个WriteBigIntegerAsString 这样的Feature,在当我转向使用第二种方式的时候发现是不生效的,这个fix修复的就是这个问题,但是我看到它已经被关掉了,希望能合并到下个版本 https://github.com/alibaba/fastjson2/pull/3876,您可以看一下我最初提的问题,里面展示了最终的输出结果

moliu avatar Dec 22 '25 02:12 moliu