FastJson 高级技巧,如何处理特殊字段如Double精度、数值N/A等(推荐收藏)

673 阅读3分钟

本文介绍一些高级技巧,用来处理一些特殊的序列化和反序列化需求。所选场景全部来自于 Github 上面的真实需求,绝对值得借鉴。

控制 Double 序列化输出的精度

开发同学,估计都曾为 Double 精度问题苦恼过。这不,有小伙伴在 Github 上面提交 ISSUE,想要自定义 Double 输出,控制小数点在两位数。

这个问题相对比较简单,只需定义一个 ValueFilter 即可。

ValueFilter filter = new ValueFilter() {
    @Override
    public Object apply(Object object, String name, Object value) {
        if (value instanceof Double) {
            BigDecimal bd = new BigDecimal(Double.toString((Double) value));
            // 保留两位小数并选择舍入模式
            BigDecimal result = bd.setScale(2, RoundingMode.HALF_UP);
            return result.toString();
        }
        return value;
    }
};

然后在序列化的时候,调用 JSON.toJSONString(Object object, Filter filter, JSONWriter.Feature... features) 这个接口即可。

上面这个接口,会把 ValueFilter 应用到所有的字段,如果发现是 Double 类型,就调用 BigDecimal 控制精度。

于是,这里有一个问题,如果只需给特定类的特定Double字段控制精度呢?这时需要用到 SerializeConfig,然后调用JSON.toJSONString(Object object, SerializeConfig config, SerializerFeature... features) 这个接口。

SerializeConfig serializeConfig = new SerializeConfig();
serializeConfig.addFilter(A.class, new DoubleFilter());

输出 Integer NULL 值为 N/A

有小伙伴在 Github 上说,在很多报表型应用中,如果某个数据没有值,在界面上打印出 null 是非常不友好的事情,更加优雅的做法是输出 N/A,那么这个应该如何实现呢?

序列化时,同样定义一个 ValueFilter 即可,这一步比较简单。

ValueFilter filter = new ValueFilter() {
    @Override
    public Object apply(Object object, String name, Object value) {
        if (value == null) {
            return "N/A"; // 如果字段值为 null,改为 "N/A"
        }
        return value;
    }
};

然而,反序列化过程中会遇到一些挑战。Fastjson 默认情况下无法识别 "N/A" 这样的特殊值,也没有为其提供内置的处理机制。这就意味着我们需要自定义一个专门的反序列化器来应对这种情况。这个自定义反序列化器不仅要能够正确解析正常的整数值,还要能够识别并适当处理 "N/A" 这样的特殊字符串,将其转换为 null 或其他适当的表示形式。

public class MyObjectReaderInt implements ObjectReader<Integer> {
    @Override
    public Integer readObject(JSONReader jsonReader, Type fieldType, Object fieldName, long features) {
        char curr = jsonReader.current();
        if (curr == '"' || curr == '\'') {
            String text =  jsonReader.readString();
            if (text.equals("N/A")) {
                return null;
            } else {
                throw new RuntimeException("unknown text " + text);
            }
        } else {
            return jsonReader.readInt32();
        }
    }
}

需要注意的是,这个反序列化器不支持全局配置,有BUG,详情参考 github.com/alibaba/fas… 。解决办法是,针对每个类的每个字段用JSONField。

总结与最佳实践

本文探讨了 FastJson 处理特殊字段的高级技巧,旨在提高 JSON 数据的可读性和实用性。这些场景源自 Github 上的真实需求,我也有幸直接参与了解决方案的贡献。

主要涵盖以下方面:

  • 精度控制:使用 ValueFilter 精确控制 Double 类型输出的小数位数
  • NULL 值处理:通过 ValueFilter 将 NULL 转换为更友好的显示(如"N/A")
  • 自定义反序列化:利用 ObjectReader 处理特殊值(如"N/A")

这些技巧能帮助开发者构建更灵活、更健壮的应用。在实际使用时,请根据具体需求选择合适的方法,并注意测试各种边界情况。

编写 ObjectReader 时需参考 Fastjson 的源码。建议使用阅读源码的神器 XCodeMap 插件 分析默认实现,然后进行针对性调整。

如果你对学习如何阅读开源项目源码、解决开源 ISSUE 或贡献开源代码感兴趣,欢迎关注我。我将持续分享我的开源经历和心得。