布尔和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提供了二合一的组合方法,一个顶两:
@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():
该方法将强制生成器不做任何修改地逐字复制输入文本(包括不进行转义,也不添加分隔符,即使上下文[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()
使用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夯实基础,毕竟做技术的要知其然,知其所以然了后,面对问题才能坦然。