jackson learning II: jackson core

Welcome to my GitHub

https://github.com/zq2599/blog_demos
Content: classification and summary of all original articles and supporting source code, involving Java, Docker, Kubernetes, DevOPS, etc;

Summary of series articles

About Jackson core

  1. The main content of this paper is Jackson core library, which is a low-level API library. It provides flow parsing tool JsonParser and flow generation tool JsonGenerator;
  2. In daily serialization and deserialization processing, Jackson annotations and Jackson databind are most commonly used, while Jackson core is not used in most cases because its API is too basic;
  3. Although Jackson databind is responsible for serialization and deserialization, its underlying implementation calls the API of Jackson core;
  4. Based on the principle of building from the ground, this article tells us about the mysterious jackson core and the basic principle of serialization and deserialization of the whole jackson;

Source download

  1. If you don't want to code, you can download all the source code in GitHub. The address and link information are shown in the table below( https://github.com/zq2599/blog_demos):
name link remarks
Project Home https://github.com/zq2599/blog_demos The project is on the home page of GitHub
git warehouse address (https) https://github.com/zq2599/blog_demos.git The warehouse address of the source code of the project, https protocol
git warehouse address (ssh) git@github.com:zq2599/blog_demos.git The warehouse address of the source code of the project, ssh protocol
  1. There are multiple folders in this git project. The application of this chapter is under the Jackson demo folder, as shown in the red box below:

Create parent-child project

Create a maven project named Jackson demo, which is a parent-child structure project with POM The XML content is as follows:

<?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>

New sub project beans

  1. Add a child project named beans under the parent project jscksondemo, which contains some constants and Pojo classes;
  2. Add class constant. For defining constants java:
package com.bolingcavalry.jacksondemo.beans;

public class Constant {
    /**
     * The value of this string is a network address, and the corresponding content of this address is JSON
     */
    public final static String TEST_JSON_DATA_URL = "https://raw.githubusercontent.com/zq2599/blog_demos/master/files/twitteer_message.json";
    /**
     * JSON string used to validate deserialization
     */
    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" +
            "}";
    /**
     * Used to validate the serialized TwitterEntry instance
     */
    public final static TwitterEntry TEST_OBJECT = new TwitterEntry();
    /**
     * Get ready for test_ Parameters of object 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. Add a Pojo, corresponding to a twitter message:
package com.bolingcavalry.jacksondemo.beans;
/**
 * @Description: Twitter message bean
 * @author: willzhao E-mail: zq2599@gmail.com
 * @date: 2020/7/4 16:24
 */
public class TwitterEntry {
    /**
     * Twitter message id
     */
    long id;
    /**
     * Message content
     */
    String text;    /**
     * Message Creator
     */
    int fromUserId;
    /**
     * Message Receiver 
     */
    int toUserId;
    /**
     * Language type
     */
    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. The above is the preparatory work. Next, start the actual combat Jackson core;

Is JsonFactory thread safe?

  1. Whether JsonFactory is thread safe or not is a question to be clarified before coding, because the creation of JsonParser and JsonGenerator are inseparable from JsonFactory;
  2. As shown in the red box in the figure below, the official jackson document clearly points out that JsonFactory is thread safe and can be safely used as a global variable for multiple threads at the same time:
  3. Official document address: http://fasterxml.github.io/jackson-core/javadoc/2.11/

Jackson core actual combat

  1. New sub project core, POM The XML is as follows:
<?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. Create a new StreamingDemo class, which contains all demos that call Jackson core's API for serialization and deserialization, as follows:
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 Use of lower order methods
 * @author: willzhao E-mail: zq2599@gmail.com
 * @date: 2020/7/4 15:50
 */
public class StreamingDemo {

    private static final Logger logger = LoggerFactory.getLogger(StreamingDemo.class);

    JsonFactory jsonFactory = new JsonFactory();

    /**
     * The value of this string is a network address, and the corresponding content of this address is JSON
     */
    final static String TEST_JSON_DATA_URL = "https://raw.githubusercontent.com/zq2599/blog_demos/master/files/twitteer_message.json";

    /**
     * JSON string used to validate deserialization
     */
    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" +
            "}";

    /**
     * Used to validate the serialized TwitterEntry instance
     */
    final static TwitterEntry TEST_OBJECT = new TwitterEntry();

    /**
     * Get ready for test_ Parameters of object 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");
    }


    /**
     * Deserialization test (JSON - > object), and the input parameter is JSON string
     * @param json JSON character string
     * @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("There are no curly braces at the start");
            throw new IOException("There are no curly braces at the start");
        }

        TwitterEntry result = new TwitterEntry();

        try {
            // Iterate over object fields:
            while (jsonParser.nextToken() != JsonToken.END_OBJECT) {

                String fieldName = jsonParser.getCurrentName();

                logger.info("Parsing fields [{}]", jsonParser.getCurrentName());

                // Parse next
                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("Unknown field '" + fieldName + "'");
                        throw new IOException("Unknown field '" + fieldName + "'");
                }
            }
        } catch (IOException e) {
            logger.error("Exception in deserialization :", e);
        } finally {
            jsonParser.close(); // important to close both parser and underlying File reader
        }

        return result;
    }

    /**
     * Deserialization test (JSON - > object), and the input parameter is JSON string
     * @param url JSON Network address of string
     * @return
     * @throws IOException
     */
    public TwitterEntry deserializeJSONFromUrl(String url) throws IOException {
        // Get JSON string from network
        String json = IOUtils.toString(new URL(TEST_JSON_DATA_URL), JsonEncoding.UTF8.name());

        logger.info("Get from network JSON data :\n{}", json);

        if(StringUtils.isNotBlank(json)) {
            return deserializeJSONStr(json);
        } else {
            logger.error("Get from network JSON Data failure");
            return null;
        }
    }


    /**
     * Serialization test (object - > JSON)
     * @param twitterEntry
     * @return JSON string obtained by object serialization
     */
    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("Exception in serialization :", e);
        } finally {
            jsonGenerator.close();
        }

        // Be sure to
        rlt = byteArrayOutputStream.toString();

        return rlt;
    }


    public static void main(String[] args) throws Exception {

        StreamingDemo streamingDemo = new StreamingDemo();

        // Perform an object to JSON operation
        logger.info("********************Perform an object conversion JSON operation********************");
        String serializeResult = streamingDemo.serialize(TEST_OBJECT);
        logger.info("The serialization result is JSON character string : \n{}\n\n", serializeResult);

        // Perform a JSON to object operation with a local string
        logger.info("********************Perform a local JSON Deserialization operation********************");
        TwitterEntry deserializeResult = streamingDemo.deserializeJSONStr(TEST_JSON_STR);
        logger.info("\n local JSON The result of deserialization is a java example : \n{}\n\n", deserializeResult);

        // Perform a JSON to object operation with the network address
        logger.info("********************Perform a network JSON Deserialization operation********************");
        deserializeResult = streamingDemo.deserializeJSONFromUrl(TEST_JSON_DATA_URL);
        logger.info("\n network JSON The result of deserialization is a java example : \n{}", deserializeResult);

        ObjectMapper a;
    }
}
  1. As can be seen from the above code, JsonParser is responsible for parsing JSON into the variable value of the object, and the core is to cycle all the contents in JSON;
  2. JsonGenerator is responsible for writing the variables of the object into the attributes of JSON. Here, the developer decides which fields to deal with;
  3. Whether it's JsonParser or JsonGenerator, you can feel that the workload is very heavy. Developers need to implement the relationship mapping between objects and JSON fields by themselves. In practical application, we don't need such hard coding. The other two libraries of jackson (the databind of announcement) have helped us complete a lot of work. The above code only reveals the most basic jackson execution principle;
  4. Execute the StreamingDemo class, and the results are as follows: serialization and deserialization are successful:
  • The above are the basic functions of jackson core. We have understood the working principle of jackson at the bottom. The next articles will continue to practice more operations;

Welcome to my official account: programmer Xinchen

Tags: Java jackson

Posted by k9underdog on Tue, 17 May 2022 20:55:02 +0300