5

jackson学习之二:jackson-core_4月月更_程序员欣宸_InfoQ写作平台

 2 years ago
source link: https://xie.infoq.cn/article/2c87db5f29a501169068fa6cd
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

jackson 学习之二:jackson-core

作者:程序员欣宸
  • 2022 年 4 月 07 日
  • 本文字数:7476 字

    阅读完需:约 25 分钟

jackson学习之二:jackson-core

欢迎访问我的 GitHub

这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos

关于 jackson-core

  1. 本文主要内容是 jackson-core 库,这是个低阶 API 库,提供流式解析工具 JsonParser,流式生成工具 JsonGenerator

  2. 在日常的序列化和反序列化处理中,最常用的是 jackson-annotations jackson-databind,而 jackson-core 由于它提供的 API 过于基础,我们大多数情况下是用不上的;

  3. 尽管 jackson-databind 负责序列化和反序列化处理,但它的底层实现是调用了 jackson-core 的 API;

  4. 本着万丈高楼平地起的原则,本文咱们通过实战了解神秘的 jackson-core,了解整个 jackson 的序列化和反序列化基本原理;

  1. 如果您不想编码,可以在 GitHub 下载所有源码,地址和链接信息如下表所示(https://github.com/zq2599/blog_demos):

  1. 这个 git 项目中有多个文件夹,本章的应用在 jacksondemo 文件夹下,如下图红框所示:

创建父子工程

  • 创建名为 jacksondemo 的 maven 工程,这是个父子结构的工程,其 pom.xml 内容如下:

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <properties>        <java.version>1.8</java.version>    </properties>    <groupId>com.bolingcavalry</groupId>    <artifactId>jacksondemo</artifactId>    <version>1.0-SNAPSHOT</version>    <packaging>pom</packaging>    <modules>        <module>core</module>        <module>beans</module>        <module>databind</module>    </modules>    <dependencyManagement>        <dependencies>            <dependency>                <groupId>com.fasterxml.jackson.core</groupId>                <artifactId>jackson-databind</artifactId>                <version>2.11.0</version>                <scope>compile</scope>            </dependency>            <dependency>                <groupId>org.slf4j</groupId>                <artifactId>slf4j-log4j12</artifactId>                <version>1.7.25</version>                <scope>compile</scope>            </dependency>            <dependency>                <groupId>commons-io</groupId>                <artifactId>commons-io</artifactId>                <version>2.7</version>                <scope>compile</scope>            </dependency>            <dependency>                <groupId>org.apache.commons</groupId>                <artifactId>commons-lang3</artifactId>                <version>3.10</version>                <scope>compile</scope>            </dependency>        </dependencies>    </dependencyManagement></project>

新增子工程 beans

  1. 在父工程 jscksondemo 下新增名为 beans 的子工程,这里面是一些常量和 Pojo 类;

  2. 增加定义常量的类 Constant.java:

package com.bolingcavalry.jacksondemo.beans;public class Constant {    /**     * 该字符串的值是个网络地址,该地址对应的内容是个JSON     */    public final static String TEST_JSON_DATA_URL = "https://raw.githubusercontent.com/zq2599/blog_demos/master/files/twitteer_message.json";    /**     * 用来验证反序列化的JSON字符串     */    public final static String TEST_JSON_STR = "{\n" +            "  \"id\":1125687077,\n" +            "  \"text\":\"@stroughtonsmith You need to add a \\\"Favourites\\\" tab to TC/iPhone. Like what TwitterFon did. I can't WAIT for your Twitter App!! :) Any ETA?\",\n" +            "  \"fromUserId\":855523, \n" +            "  \"toUserId\":815309,\n" +            "  \"languageCode\":\"en\"\n" +            "}";    /**     * 用来验证序列化的TwitterEntry实例     */    public final static TwitterEntry TEST_OBJECT = new TwitterEntry();    /**     * 准备好TEST_OBJECT对象的各个参数     */    static {        TEST_OBJECT.setId(123456L);        TEST_OBJECT.setFromUserId(101);        TEST_OBJECT.setToUserId(102);        TEST_OBJECT.setText("this is a message for serializer test");        TEST_OBJECT.setLanguageCode("zh");    }}
  1. 增加一个 Pojo,对应的是一条推特消息:

package com.bolingcavalry.jacksondemo.beans;/** * @Description: 推特消息bean * @author: willzhao E-mail: [email protected] * @date: 2020/7/4 16:24 */public class TwitterEntry {    /**     * 推特消息id     */    long id;    /**     * 消息内容     */    String text;    /**     * 消息创建者     */    int fromUserId;    /**     * 消息接收者     */    int toUserId;    /**     * 语言类型     */    String languageCode;    public long getId() {        return id;    }    public void setId(long id) {        this.id = id;    }    public String getText() {        return text;    }    public void setText(String text) {        this.text = text;    }    public int getFromUserId() {        return fromUserId;    }    public void setFromUserId(int fromUserId) {        this.fromUserId = fromUserId;    }    public int getToUserId() {        return toUserId;    }    public void setToUserId(int toUserId) {        this.toUserId = toUserId;    }    public String getLanguageCode() {        return languageCode;    }    public void setLanguageCode(String languageCode) {        this.languageCode = languageCode;    }    public TwitterEntry() {    }    public String toString() {        return "[Tweet, id: "+id+", text='"+text+"', from: "+fromUserId+", to: "+toUserId+", lang: "+languageCode+"]";    }}
  1. 以上就是准备工作了,接下来开始实战 jackson-core;

JsonFactory 线程安全吗?

  1. JsonFactory 是否是线程安全的,这是编码前要弄清楚的问题,因为 JsonParser JsonGenerator 的创建都离不开 JsonFactory;

  2. 如下图红框所示,jackson 官方文档中明确指出 JsonFactory 是线程安全的,可以放心的作为全局变量给多线程同时使用:

  3. 官方文档地址:http://fasterxml.github.io/jackson-core/javadoc/2.11/

jackson-core 实战

  1. 新建子工程 core,pom.xml 如下:

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0"         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">    <parent>        <artifactId>jacksondemo</artifactId>        <groupId>com.bolingcavalry</groupId>        <version>1.0-SNAPSHOT</version>        <relativePath>../pom.xml</relativePath>    </parent>    <modelVersion>4.0.0</modelVersion>    <groupId>com.bolingcavalry</groupId>    <artifactId>core</artifactId>    <name>core</name>    <description>Demo project for jackson core use</description>    <build>        <plugins>            <plugin>                <groupId>org.apache.maven.plugins</groupId>                <artifactId>maven-compiler-plugin</artifactId>                <configuration>                    <source>8</source>                    <target>8</target>                </configuration>            </plugin>        </plugins>    </build>    <dependencies>        <dependency>            <groupId>com.fasterxml.jackson.core</groupId>            <artifactId>jackson-databind</artifactId>        </dependency>        <dependency>            <groupId>org.slf4j</groupId>            <artifactId>slf4j-log4j12</artifactId>        </dependency>        <dependency>            <groupId>commons-io</groupId>            <artifactId>commons-io</artifactId>        </dependency>        <dependency>            <groupId>org.apache.commons</groupId>            <artifactId>commons-lang3</artifactId>        </dependency>        <dependency>            <groupId>com.bolingcavalry</groupId>            <artifactId>beans</artifactId>            <version>${project.version}</version>        </dependency>    </dependencies></project>
  1. 新建 StreamingDemo 类,这里面是调用 jackson-core 的 API 进行序列化和反序列化的所有 demo,如下:

package com.bolingcavalry.jacksondemo.core;import com.bolingcavalry.jacksondemo.beans.TwitterEntry;import com.fasterxml.jackson.core.*;import com.fasterxml.jackson.databind.ObjectMapper;import org.apache.commons.io.IOUtils;import org.apache.commons.lang3.StringUtils;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.net.URL;/** * @Description: jackson低阶方法的使用 * @author: willzhao E-mail: [email protected] * @date: 2020/7/4 15:50 */public class StreamingDemo {    private static final Logger logger = LoggerFactory.getLogger(StreamingDemo.class);    JsonFactory jsonFactory = new JsonFactory();    /**     * 该字符串的值是个网络地址,该地址对应的内容是个JSON     */    final static String TEST_JSON_DATA_URL = "https://raw.githubusercontent.com/zq2599/blog_demos/master/files/twitteer_message.json";    /**     * 用来验证反序列化的JSON字符串     */    final static String TEST_JSON_STR = "{\n" +            "  \"id\":1125687077,\n" +            "  \"text\":\"@stroughtonsmith You need to add a \\\"Favourites\\\" tab to TC/iPhone. Like what TwitterFon did. I can't WAIT for your Twitter App!! :) Any ETA?\",\n" +            "  \"fromUserId\":855523, \n" +            "  \"toUserId\":815309,\n" +            "  \"languageCode\":\"en\"\n" +            "}";    /**     * 用来验证序列化的TwitterEntry实例     */    final static TwitterEntry TEST_OBJECT = new TwitterEntry();    /**     * 准备好TEST_OBJECT对象的各个参数     */    static {        TEST_OBJECT.setId(123456L);        TEST_OBJECT.setFromUserId(101);        TEST_OBJECT.setToUserId(102);        TEST_OBJECT.setText("this is a message for serializer test");        TEST_OBJECT.setLanguageCode("zh");    }    /**     * 反序列化测试(JSON -> Object),入参是JSON字符串     * @param json JSON字符串     * @return     * @throws IOException     */    public TwitterEntry deserializeJSONStr(String json) throws IOException {        JsonParser jsonParser = jsonFactory.createParser(json);        if (jsonParser.nextToken() != JsonToken.START_OBJECT) {            jsonParser.close();            logger.error("起始位置没有大括号");            throw new IOException("起始位置没有大括号");        }        TwitterEntry result = new TwitterEntry();        try {            // Iterate over object fields:            while (jsonParser.nextToken() != JsonToken.END_OBJECT) {                String fieldName = jsonParser.getCurrentName();                logger.info("正在解析字段 [{}]", jsonParser.getCurrentName());                // 解析下一个                jsonParser.nextToken();                switch (fieldName) {                    case "id":                        result.setId(jsonParser.getLongValue());                        break;                    case "text":                        result.setText(jsonParser.getText());                        break;                    case "fromUserId":                        result.setFromUserId(jsonParser.getIntValue());                        break;                    case "toUserId":                        result.setToUserId(jsonParser.getIntValue());                        break;                    case "languageCode":                        result.setLanguageCode(jsonParser.getText());                        break;                    default:                        logger.error("未知字段 '" + fieldName + "'");                        throw new IOException("未知字段 '" + fieldName + "'");                }            }        } catch (IOException e) {            logger.error("反序列化出现异常 :", e);        } finally {            jsonParser.close(); // important to close both parser and underlying File reader        }        return result;    }    /**     * 反序列化测试(JSON -> Object),入参是JSON字符串     * @param url JSON字符串的网络地址     * @return     * @throws IOException     */    public TwitterEntry deserializeJSONFromUrl(String url) throws IOException {        // 从网络上取得JSON字符串        String json = IOUtils.toString(new URL(TEST_JSON_DATA_URL), JsonEncoding.UTF8.name());        logger.info("从网络取得JSON数据 :\n{}", json);        if(StringUtils.isNotBlank(json)) {            return deserializeJSONStr(json);        } else {            logger.error("从网络获取JSON数据失败");            return null;        }    }    /**     * 序列化测试(Object -> JSON)     * @param twitterEntry     * @return 由对象序列化得到的JSON字符串     */    public String serialize(TwitterEntry twitterEntry) throws IOException{        String rlt = null;        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();        JsonGenerator jsonGenerator = jsonFactory.createGenerator(byteArrayOutputStream, JsonEncoding.UTF8);        try {            jsonGenerator.useDefaultPrettyPrinter();            jsonGenerator.writeStartObject();            jsonGenerator.writeNumberField("id", twitterEntry.getId());            jsonGenerator.writeStringField("text", twitterEntry.getText());            jsonGenerator.writeNumberField("fromUserId", twitterEntry.getFromUserId());            jsonGenerator.writeNumberField("toUserId", twitterEntry.getToUserId());            jsonGenerator.writeStringField("languageCode", twitterEntry.getLanguageCode());            jsonGenerator.writeEndObject();        } catch (IOException e) {            logger.error("序列化出现异常 :", e);        } finally {            jsonGenerator.close();        }        // 一定要在        rlt = byteArrayOutputStream.toString();        return rlt;    }    public static void main(String[] args) throws Exception {        StreamingDemo streamingDemo = new StreamingDemo();        // 执行一次对象转JSON操作        logger.info("********************执行一次对象转JSON操作********************");        String serializeResult = streamingDemo.serialize(TEST_OBJECT);        logger.info("序列化结果是JSON字符串 : \n{}\n\n", serializeResult);        // 用本地字符串执行一次JSON转对象操作        logger.info("********************执行一次本地JSON反序列化操作********************");        TwitterEntry deserializeResult = streamingDemo.deserializeJSONStr(TEST_JSON_STR);        logger.info("\n本地JSON反序列化结果是个java实例 : \n{}\n\n", deserializeResult);        // 用网络地址执行一次JSON转对象操作        logger.info("********************执行一次网络JSON反序列化操作********************");        deserializeResult = streamingDemo.deserializeJSONFromUrl(TEST_JSON_DATA_URL);        logger.info("\n网络JSON反序列化结果是个java实例 : \n{}", deserializeResult);        ObjectMapper a;    }}
  1. 上述代码可见 JsonParser 负责将 JSON 解析成对象的变量值,核心是循环处理 JSON 中的所有内容;

  2. JsonGenerator 负责将对象的变量写入 JSON 的各个属性,这里是开发者自行决定要处理哪些字段;

  3. 不论是 JsonParser 还是 JsonGenerator,大家都可以感觉到工作量很大,需要开发者自己动手实现对象和 JSON 字段的关系映射,实际应用中不需要咱们这样辛苦的编码,jackson 的另外两个库(annonation 的 databind)已经帮我们完成了大量工作,上述代码只是揭示最基础的 jackson 执行原理;

  4. 执行 StreamingDemo 类,得到结果如下,序列化和反序列化都成功了:

  • 以上就是 jackson-core 的基本功能,咱们了解了 jackson 最底层的工作原理,接下来的文章会继续实践更多操作;

欢迎关注 InfoQ:程序员欣宸

学习路上,你不孤单,欣宸原创一路相伴...

划线
评论
复制
发布于: 2022 年 04 月 07 日阅读数: 1310

版权声明: 本文为 InfoQ 作者【程序员欣宸】的原创文章。

原文链接:【https://xie.infoq.cn/article/2c87db5f29a501169068fa6cd】。文章转载请联系作者。

用户头像avatar-icon-number-1.a6aec119.png

搜索"程序员欣宸",一起畅游Java宇宙 2018.04.19 加入

前腾讯、前阿里员工,从事Java后台工作,对Docker和Kubernetes充满热爱,所有文章均为作者原创,个人Github:https://github.com/zq2599/blog_demos


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK