2. 妈呀,Jackson原来是这样写JSON的(下)

简介: 2. 妈呀,Jackson原来是这样写JSON的(下)

布尔和null

比较简单,JsonGenerator各提供了一个方法供你使用:


public abstract void writeBoolean(boolean state) throws IOException;
public abstract void writeNull() throws IOException;

示例代码:

@Test
public void test7() throws IOException {
    JsonFactory factory = new JsonFactory();
    try (JsonGenerator jsonGenerator = factory.createGenerator(System.out, JsonEncoding.UTF8)) {
        jsonGenerator.writeStartObject();
        jsonGenerator.writeFieldName("success");
        jsonGenerator.writeBoolean(true);
        jsonGenerator.writeFieldName("myName");
        jsonGenerator.writeNull();
        jsonGenerator.writeEndObject();
    }
}


运行程序,输出:

{"success":true,"myName":null}



组合写JSON Key和Value


在写每个value之前,都必须写key。为了简化书写,JsonGenerator提供了二合一的组合方法,一个顶两:


image.png


@Test
public void test8() throws IOException {
    JsonFactory factory = new JsonFactory();
    try (JsonGenerator jsonGenerator = factory.createGenerator(System.out, JsonEncoding.UTF8)) {
        jsonGenerator.writeStartObject();
        jsonGenerator.writeStringField("zhName","A哥");
        jsonGenerator.writeBooleanField("success",true);
        jsonGenerator.writeNullField("myName");
        // jsonGenerator.writeObjectFieldStart();
        // jsonGenerator.writeArrayFieldStart();
        jsonGenerator.writeEndObject();
    }
}


运行程序,输出:

{"zhName":"A哥","success":true,"myName":null}



实际使用时,推荐使用这些组合方法去简化书写,毕竟新盖中盖高钙片,一片能顶过去2片,效率高


其它写方法


如果说上面写方法是必修课,那下面的write写方法就当选修课吧。


writeRaw()和writeRawValue()


image.png


该方法将强制生成器不做任何修改地逐字复制输入文本(包括不进行转义,也不添加分隔符,即使上下文[array,object]可能需要这样做)。如果需要这样的分隔符,请改用writeRawValue方法。


绝大多数情况下,使用writeRaw()就够了,writeRawValue的使用场景愈发的少


@Test
public void test9() throws IOException {
    JsonFactory factory = new JsonFactory();
    try (JsonGenerator jsonGenerator = factory.createGenerator(System.out, JsonEncoding.UTF8)) {
        jsonGenerator.writeRaw("{'name':'YourBatman'}");
    }
}


运行程序,输出:

{'name':'YourBatman'}


如果换成writeString()方法,结果为(请注意比较差异):

"{'name':'YourBatman'}"


writeBinary()


image.png


使用Base64编码把数据写进去。


writeEmbeddedObject()


2.8版本新增的方法。看看此方法的源码你就知道它是什么意思,不解释:


public void writeEmbeddedObject(Object object) throws IOException {
    // 01-Sep-2016, tatu: As per [core#318], handle small number of cases
    if (object == null) {
        writeNull();
        return;
    }
    if (object instanceof byte[]) {
        writeBinary((byte[]) object);
        return;
    }
    throw new JsonGenerationException(...);
}


writeObject()(重要):

写POJO,但前提是你必须给JsonGenerator指定一个ObjectCodec解码器才能正常work,否则抛出异常:


java.lang.IllegalStateException: No ObjectCodec defined for the generator, can only serialize simple wrapper types (type passed cn.yourbatman.jackson.core.beans.User)
  at com.fasterxml.jackson.core.JsonGenerator._writeSimpleObject(JsonGenerator.java:2238)
  at com.fasterxml.jackson.core.base.GeneratorBase.writeObject(GeneratorBase.java:391)
  ...

值得注意的是,Jackson里我们最为熟悉的API ObjectMapper它就是一个ObjectCodec解码器,具体我们在数据绑定章节会再详细讨论,下面我给出个简单的使用示例模拟一把:


准备一个User对象,以及解码器UserObjectCodec:


@Data
public class User {
    private String name = "YourBatman";
    private Integer age = 18;
}
// 自定义ObjectCodec解码器 用于把User写为JSON
// 因为本例只关注write写,因此只需要实现此这一个方法即可
public class UserObjectCodec extends ObjectCodec {
  ...
    @Override
    public void writeValue(JsonGenerator gen, Object value) throws IOException {
        User user = User.class.cast(value);
        gen.writeStartObject();
        gen.writeStringField("name",user.getName());
        gen.writeNumberField("age",user.getAge());
        gen.writeEndObject();
    }
  ...
}


测试用例:

@Test
public void test11() throws IOException {
    JsonFactory factory = new JsonFactory();
    try (JsonGenerator jsonGenerator = factory.createGenerator(System.err, JsonEncoding.UTF8)) {
        jsonGenerator.setCodec(new UserObjectCodec());
        jsonGenerator.writeObject(new User());
    }
}


运行程序,输出:

{"name":"YourBatman","age":18}




😄这就是ObjectMapper的原理雏形,是不是开始着道了?😄


writeTree():

顾名思义,它便是Jackson大名鼎鼎的树模型。可惜的是core模块并没有提供树模型TreeNode的实现,以及它也是得依赖于ObjectCodec才能正常完成解码。


方法用来编写给定的JSON树(表示为树,其中给定的JsonNode是根)。这通常只调用给定节点的writeObject,但添加它是为了方便起见,并使代码在专门处理树的情况下更显式。


可能你会想,已经有了writeObject()方法还要它干啥呢?这其实是蛮有必要的,因为有时候你并不想定义POJO时,就可以用它快速写/读数据,同时它也可以达到模糊掉类型的概念,做到更抽象和更公用。


说到模糊掉类型的的操作,你也可以辅以Spring的AnnotationAttributes的设计和使用来理解


准备一个TreeNode的实现UserTreeNode:

public class UserTreeNode implements TreeNode {
    private User user;
    public User getUser() {
        return user;
    }
    public UserTreeNode(User user) {
        this.user = user;
    }
    ...
}


UserObjectCodec改写如下:


public class UserObjectCodec extends ObjectCodec {
  ...
    @Override
    public void writeValue(JsonGenerator gen, Object value) throws IOException {
        User user = null;
        if (value instanceof User) {
            user = User.class.cast(value);
        } else if (value instanceof TreeNode) {
            user = UserTreeNode.class.cast(value).getUser();
        }
        gen.writeStartObject();
        gen.writeStringField("name", user.getName());
        gen.writeNumberField("age", user.getAge());
        gen.writeEndObject();
    }
  ...
}


书写测试用例:

@Test
public void test12() throws IOException {
    JsonFactory factory = new JsonFactory();
    try (JsonGenerator jsonGenerator = factory.createGenerator(System.err, JsonEncoding.UTF8)) {
        jsonGenerator.setCodec(new UserObjectCodec());
        jsonGenerator.writeObject(new UserTreeNode(new User()));
    }
}


运行程序,输出:

{"name":"YourBatman","age":18}




本案例绕过了TreeNode的真实处理逻辑,是因为树模型这块会放在databind数据绑定模块进行更加详细的描述,后面再会喽。


说明:Jackson的树模型是比较重要的,当然直接使用core模块的树模型没有意义,所以这里先卖个关子,保持好奇心哈😄


思考题


国人很喜欢把Jackson的序列化(写JSON)效率和Fastjson进行对比,那么你敢使用本文的流式API和Fastjson比吗?结果你猜一下呢?


总结


本文介绍了jackson-core模块的流式API,以及JsonGenerator写JSON的使用,相信对你理解Jackson生成JSON方面是有帮助的。它作为JSON处理的基石,虽然并不推荐直接使用,但仅仅是应用开发级别不推荐哦,如果你是个框架、中间件开发者,这些原理你很可能绕不过。


还是那句话,本文介绍它的目的并不是建议大家去项目上使用,而是为了后面理解ObjectMapper夯实基础,毕竟做技术的要知其然,知其所以然了后,面对问题才能坦然。

相关文章
|
6月前
|
XML JSON 前端开发
Ajax技术【Ajax技术详解、 Ajax 的使用、Ajax请求、 JSON详解、JACKSON 的使用 】(一)-全面详解(学习总结---从入门到深化)
Ajax技术【Ajax技术详解、 Ajax 的使用、Ajax请求、 JSON详解、JACKSON 的使用 】(一)-全面详解(学习总结---从入门到深化)
143 1
|
23天前
|
JSON JavaScript Java
在Java中处理JSON数据:Jackson与Gson库比较
本文介绍了JSON数据交换格式及其在Java中的应用,重点探讨了两个强大的JSON处理库——Jackson和Gson。文章详细讲解了Jackson库的核心功能,包括数据绑定、流式API和树模型,并通过示例演示了如何使用Jackson进行JSON解析和生成。最后,作者分享了一些实用的代码片段和使用技巧,帮助读者更好地理解和应用这些工具。
在Java中处理JSON数据:Jackson与Gson库比较
|
3月前
|
JSON Java API
Jackson:SpringBoot中的JSON王者,优雅掌控数据之道
【8月更文挑战第29天】在Java的广阔生态中,SpringBoot以其“约定优于配置”的理念,极大地简化了企业级应用的开发流程。而在SpringBoot处理HTTP请求与响应的过程中,JSON数据的序列化和反序列化是不可或缺的一环。在众多JSON处理库中,Jackson凭借其高效、灵活和强大的特性,成为了SpringBoot中处理JSON数据的首选。今天,就让我们一起深入探讨Jackson如何在SpringBoot中优雅地控制JSON数据。
109 0
|
5月前
|
JSON fastjson 数据格式
使用jackson和fastjson实现list与json互转
使用jackson和fastjson实现list与json互转
|
6月前
|
JSON 安全 JavaScript
Java一分钟之-JSON处理:Gson与Jackson库
本文对比介绍了Java中常用的两个JSON库Gson和Jackson。Gson以其简洁易用和自动序列化/反序列化功能受到青睐,而Jackson则以优异性能和丰富功能(如字段忽略、日期格式化)著称。文中通过代码示例展示了两者的基本用法,并讨论了常见问题及解决策略,包括时间格式处理、循环引用和类型匹配。在实际应用中,应根据性能需求、安全性和版本兼容性选择合适的库,并遵循最佳实践。
182 0
|
6月前
|
JSON fastjson Java
Spring Boot Jackson 和Fast JSON 用哪个好啊 ?
【4月更文挑战第22天】
989 1
|
6月前
|
JSON Java Maven
使用Jackson进行 JSON 序列化和反序列化
使用Jackson进行 JSON 序列化和反序列化
126 0
|
XML JSON Java
Jackson 框架,轻易转换JSON
Jackson 框架,轻易转换JSON Jackson可以轻松的将Java对象转换成json对象和xml文档,同样也可以将json、xml转换成Java对象。 前面有介绍过json-lib这个框架,在线博文:http://www.cnblogs.com/hoojo/archive/2011/04/21/2023805.html 相比json-lib框架,Jackson所依赖的jar包较少,简单易用并且性能也要相对高些。
3024 0
|
18天前
|
数据采集 JSON 数据处理
抓取和分析JSON数据:使用Python构建数据处理管道
在大数据时代,电商网站如亚马逊、京东等成为数据采集的重要来源。本文介绍如何使用Python结合代理IP、多线程等技术,高效、隐秘地抓取并处理电商网站的JSON数据。通过爬虫代理服务,模拟真实用户行为,提升抓取效率和稳定性。示例代码展示了如何抓取亚马逊商品信息并进行解析。
抓取和分析JSON数据:使用Python构建数据处理管道