在 Java 生态中,Jackson 一直是处理 JSON 数据的明星库。随着 Jackson 3 的发布进入实质性阶段,一系列 API 变动引发了开发社区的热烈讨论。其中最受关注的一个细节问题是:如果只依赖 jackson-core(而不引入 jackson-databind),在 Jackson 3 中究竟该如何“地道地”创建一个 JsonParser 实例? 这个问题看似微小,却折射出 Jackson 3 在模块职责划分上的深层变革。

从“随意用”到“明确归属”:模块解耦的代价

在 Jackson 2.x 时代,JsonFactorycreateParser 方法对许多开发者而言几乎是“肌肉记忆”。即便项目只引入了 jackson-core,也能通过 new JsonFactory().createParser(jsonString) 轻松获得 JsonParser。这一接口长期稳定,以至于很多人并未意识到它其实属于“高阶”功能——因为 JsonFactory 的实例化过程还涉及特征标记、输入源封装等隐式逻辑。

Jackson 3 在设计理念上更强调模块的“单一职责”。核心模块 jackson-core 不再承担 JsonParser 的“工厂”角色,而是将这一职责下放给了专门的“构建器”(Builder)或更高层次的模块。具体来说,在 Jackson 3 中,如果你只依赖 jackson-coreJsonFactory 本身仍然存在,但它的 createParser 方法要么被标记为已弃用,要么要求传入一个预先配置好的 JsonReadContext 对象,使调用变得繁琐且不直观。

社区中有人吐槽:“这是一个典型的‘解构’陷阱——理论上模块更清晰了,但日常编码却连一个简单的解析器都创建得磕磕绊绊。”

官方推荐:TokenBuffer 与 JsonFactory 的新搭档

在 Jackson 3 的官方文档和早期公开讨论中,推荐的方式是使用 TokenBuffer 或直接通过 JsonFactory.Builder 来构造。具体步骤如下:

  1. 通过 JsonFactory.builder() 获取工厂构建器:这是 Jackson 3 引入的新工厂模式。JsonFactory 不再提供无参构造函数(或将其降级为受保护),而是强制要求通过 builder() 方法配置后再构建。
  2. 调用 builder().build() 获得 JsonFactory 实例
  3. 利用该实例的 createParser 方法:但请注意,此时的 createParser 只接受 InputStreamReader 等流式输入,不再支持 Stringbyte[] 直接解析——这些便捷重载被移到了 jackson-databindObjectMapper 中。

换句话说,如果你只使用 jackson-core,解析一个 JSON 字符串需要先将其包装成 ByteArrayInputStream,再调用 createParser(inputStream)。这比 Jackson 2 多了一步转换,显得不够“优雅”。

社区的另类解法:回退到 Streaming API 的底层

部分开发者在 Stack Overflow 和 GitHub Issue 中提出了替代方案:直接使用 JsonParser 的静态工厂方法 JsonParser.create()。但这个 create() 方法在 Jackson 3 中同样不存在——它属于一个更底层的内部 API。实际上,Jackson 3 希望用户通过 JsonFactory 这一“安全网关”来获取 JsonParser,以避免直接操作内部状态。

也有开发者选择干脆升级依赖,引入 jackson-databind,然后使用 new ObjectMapper().getFactory().createParser(string) 这种“曲线救国”的方式。但这显然违背了“仅用 jackson-core”的前提。

变通之道:先理解设计意图

从 Jackson 团队的角度来看,这种变化是合理的。jackson-core 定义的是流式 API 的基础接口(如 JsonTokenJsonParserJsonGenerator),而 JsonFactory 属于“实现工厂”,其行为受到 jackson-databind 中大量配置的制约。为了让核心模块彻底独立,必须切断工厂对字符串等“高级”输入类型的默认支持。

因此,如果你真的只需要 jackson-core,最符合设计“惯用方式”的方案是:

JsonFactory factory = JsonFactory.builder().build();
JsonParser parser = factory.createParser(new ByteArrayInputStream(jsonString.getBytes(StandardCharsets.UTF_8)));

或者,如果项目允许接受 Reader

JsonParser parser = factory.createParser(new StringReader(jsonString));

这两种方式都严格限定在 jackson-core 范围内,没有额外依赖,且体现了 Jackson 3 “显式处理输入流” 的哲学。

启示:拥抱变化,读文档比问社区更重要

这一看似琐碎的 API 变动,实则是 Jackson 3 向更干净、更模块化架构迈进的缩影。对于习惯“无脑调用”的老用户而言,初期确实会感到不适,但一旦接受了“输入源必须显式转换”的设定,代码的意图反而变得更加清晰:你从“我要解析这段字符串”变成了“我要从这段字节流中解析JSON”。

目前 Jackson 3 仍处于 Release Candidate 阶段,最终 API 可能还有微调。但无论如何,开发者都应重新审视自己项目的依赖树:你真的需要 jackson-databind 吗?如果只是做基础流式处理,那么掌握 jackson-core 的新范式,将让你在未来版本迁移中少踩很多坑。毕竟,这才是一个“地道”的 Jackson 3 用户应有的姿态。