likes
comments
collection
share

我解决了 FastJson Issue:空对象序列化为空数组,应该怎么写工作很多年,一直CRUD,实在没意思,也没前途。

作者站长头像
站长
· 阅读数 18

工作很多年,一直CRUD,实在没意思,也没前途。最近得一大佬指点,开始参与开源项目,从解决 Issue 开始。

这不,真的逮到机会了。有人在 FastJson 的 Github 主页上面提交了一个需求,想把空对象序列化为空数组,应该怎么写?

具体的需求如下图:

我解决了 FastJson Issue:空对象序列化为空数组,应该怎么写工作很多年,一直CRUD,实在没意思,也没前途。

我第一反应是,这都是什么鬼问题啊。不过,冷静下来想,一般开发遇到问题,都是问搜索引擎和AI。

能跑到 Github 上面来提问题,首先说明,这个程序员,有参与开源社区的习惯,对开源社区的运作机制也有一定的理解,这一点值得赞赏。其次说明,这个问题,恐怕没有那么简单。

问题分析

我仔细分析了一下这个问题,结合以往的开发经验,很快就意识到这个需求可能需要实现一个自定义的 ObjectReader。

然而,当我真正开始着手实施时,很快就遇到了一个不小的挑战。ObjectReader 的实现远比想象中复杂,它并不遵循常规的编程模式。这种复杂性主要源于 FastJson 在设计上对性能的极致追求,导致了一些非常规的实现方式。

我曾看过 Jackson 的源码,其反序列化逻辑相对直观:它首先将 JSON 字符串解析成一个 JsonNode 树状结构,然后再将这个树转化为 Java 对象。这种两步走的方法虽然可能在性能上有所损失,但大大简化了自定义序列化和反序列化的实现。

相比之下,FastJson 采用了一种更为直接的方法。它没有中间的树状结构这一步骤,而是直接从 JSON 字符串到 Java 对象。这种设计虽然提高了性能,但也使得自定义 ObjectReader 的实现变得更加复杂

这让我有些打退堂鼓!

好在这时候,大佬指导我说,在源码里找找近似实现,然后再去改改。

参考实现

我找了找,果然找到了一个 ObjectReaderImplListStr,这个实现是序列化 String 数组。在里面有这样的 If-else 操作。

char ch = jsonReader.current();
if (ch == '[') {
    jsonReader.next();
    while (!jsonReader.nextIfArrayEnd()) {
        String item = jsonReader.readString();
        if (item == null && list instanceof SortedSet) {
            continue;
        }
        list.add(item);
    }
} else if (ch == '"' || ch == '\'' || ch == '{') {
    String str = jsonReader.readString();
    if (str != null && !str.isEmpty()) {
        list.add(str);
    }
} 

如果发现当前字符不是’[’,则把余下的内容当成一个字符串。

这个需求,与原 ISSUE 提交者的需求非常接近了,我只改了一行代码,把 list.add 注释掉,因为原ISSUE 需要返回的是空数组。

else if (ch == '"' || ch == '\'' || ch == '{') {
        String str = jsonReader.readString();
        if (str != null && !str.isEmpty()) {
            //list.add(str);
        }
    }

于是,我写了一个 MyListReader,并使用注解 JSONField 添加到了字段上:

@JSONField(deserializeUsing = MyListReader.class)
private List<Integer> list;

一切准备就绪,开始运行,可是结果却不符合预期!

Range{start=2, end=1, list=[{}]}

很显然,预期返回 list=[], 但 FastJson 把 ”{}” 当成一个字符串返回了,MyListReader 并没有被用上!

这是为啥呢?

大佬丢给我一个走读源码的工具,XCodeMap,让我趁此机会,走读一下代码。

走读源码

XCodeMap 确实是一个走读代码的神器,它会把程序走过的函数给高亮出来,一眼看出分支走向,快速略过无关的分支,并通过查看定义,直接找到当前的实现。

由于免除了分支抽象的困扰,我很快找到了一行关键代码,所有基于 field 的 FieldWriter 都被忽略了。

我解决了 FastJson Issue:空对象序列化为空数组,应该怎么写工作很多年,一直CRUD,实在没意思,也没前途。

再利用 XCodeMap 的对象追踪能力,很快可以查到 fieldInfo 设置成ignore是由于以下两行代码:

fieldInfo.init();
fieldInfo.ignore = (field.getModifiers() & Modifier.PUBLIC) == 0 || (field.getModifiers() & Modifier.TRANSIENT) != 0;

看代码,应该就猜得出来,“非 public” 或者 “transient” 的字段都会被忽略。

也就是说,我设置的 MyListReader 被忽略了。

于是,我把字段属性改成 public,再运行一次,果然OK了。

解决办法

终极解决办法,当然不是把字段改成 public,而是把注释加到方法上面,类似于下面这样:

@JSONField(deserializeUsing = MyListReader.class)
public void setList(List<String> list) {
    this.list = list;
}

问题圆满解决。最终,我把答案贴到了 Issue 里面,原ISSUE 作者,也给我表达了感谢。

我解决了 FastJson Issue:空对象序列化为空数组,应该怎么写工作很多年,一直CRUD,实在没意思,也没前途。

这还是第一次,用自己的技术在开源社区,帮助到别人,真的是非常开心。

经历总结

参与开源项目,确实是一个很好的学习机会。这次经历也让我体会到了解决复杂问题的过程。从最初的困惑到最终的解决,我经历了问题分析、寻找参考实现、源码走读等多个阶段。这个过程不仅锻炼了我的技术能力,还提升了我的问题解决能力和耐心。

这次经历也让我认识到了工具的重要性。无论是 Github 这样的开源平台,还是 XCodeMap 这样的源码阅读工具,都极大地提高了我的工作效率。在今后的工作中,我会更加注重工具的选择和使用,以此来提升自己的工作效率和代码质量。

参考:

  1. github.com/alibaba/fas…
  2. xcodemap.tech/
转载自:https://juejin.cn/post/7423706686173003810
评论
请登录