In the previous article, After much research, we decided to disable FastJson! “, explained the basic use of FastJson and the existence of uncertainty, so the final decision to give up the use of the project, and then choose the market more mainstream, Spring Boot default binding JSON class library: Jackson.

This article will explain the basic use of Jackson and the combination and practice with Spring Boot.

What is Jackson

Jackson is one of the more mainstream Java-based JSON libraries for serialization and deserialization of JSON and XML with Javabeans.

Yes, Jackson can also handle transformations between Javabeans and XML, based on the Jackson-dataformation-XML component, and is more efficient and secure than the JDK’s native XML implementation. What we use a lot is the functionality between JSON and Javabeans.

How mainstream was Jackson? Just looking at Maven’s repository statistics, Jackson ranks first in usage. Of the three JSON libraries supported by Spring Boot (Gson, Jackson, jSON-B), Jackson is the preferred default library.

Jackson also has the following features: low dependency, easy to use, fast parsing of large Json, low memory footprint, flexible API, easy to expand and customize.

Jackson library GitHub address: github.com/FasterXML/j… .

Part of Jackson

The Jackson core module consists of three parts (starting from Jackson 2.x) : Jackson-core, Jackson-Annotations, and Jackson-Databind.

  • Jackson-core: Core package that defines a low-level Streaming API and provides “stream mode” based parsing. The Jackson internal implementation generates and parses JSON using JsonGenerator and JsonParser, the high-performance stream mode API.
  • Annotations package, which provides standard Jackson annotations;
  • Jackson-databind: Data binding (Databind) package, which implements data binding (and object serialization) support and relies on Streaming and Annotations packages. Provides apis based on “object binding” parsing (ObjectMapper) and “tree model” parsing (JsonNode); The API based on “object binding” parsing and the API based on “tree model” parsing rely on the API based on “stream pattern” parsing.

Let’s look at the dependency introduction of related components in different environments.

In SpringBoot, spring-boot-starter-Web indirectly introduces Jackson components, meaning that if you use the SpringBoot framework, you already have Jackson dependencies in your project. The following dependencies omit the Version and scope items.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
Copy the code

The Web starter relies on json starter:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-json</artifactId>
</dependency>
Copy the code

Json Starter finally introduced Jackson:

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
  <groupId>com.fasterxml.jackson.datatype</groupId>
  <artifactId>jackson-datatype-jdk8</artifactId>
</dependency>
<dependency>
  <groupId>com.fasterxml.jackson.datatype</groupId>
  <artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<dependency>
  <groupId>com.fasterxml.jackson.module</groupId>
  <artifactId>jackson-module-parameter-names</artifactId>
</dependency>
Copy the code

As mentioned above, Jackson-Databind relies on Streaming and Annotations packages, so introducing jackson-Databind is equivalent to introducing Jackson-core and Jackson-Annotations.

Typically, to use it alone, Maven imports jackson-Databind, jackson-core, and Jackson-Annotations as needed.

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>
Copy the code

For SpringBoot projects, there are almost no additional dependencies.

Jackson core class ObjectMapper

Jackson offers three ways to handle JSON: data binding, JSON tree model, and streaming API. The first two functions are implemented based on ObjectMapper, while the streaming API functions need to be implemented based on the underlying JsonGenerator and JsonParser.

It is usually enough to use the ObjectMapper class, which has the following functionality:

  • Parse JSON from a string, stream, or file and create a Java object that represents the parsed JSON (deserialization).
  • Build a Java object into a JSON string (serialization).
  • Parse JSON as objects of custom classes, as well as objects of JSON tree models;

ObjectMapper is based on JsonParser and JsonGenerator to implement the actual JSON read/write. You can see this by looking at the ObjectMapper constructor.

Specific instance

Instead of going through the common uses of Jackson, I’ll show you a series of examples, each of which will be annotated.

Common simple use

The following example is a common usage demonstration that involves converting between Javabeans and Json strings.

When Jackson converts JSON to JavaBean properties, the default binding is that the name of the JSON field matches the getter and setter methods in the Java object.

Jackson removes the “get” and “set” parts from getter and setter method names and lowercase the first letter. For example, name in Json matches getName() and setName() in JavaBean.

However, not all attributes can be serialized and deserialized, basically following the following rules:

  • Attributes modified by public can be serialized and deserialized.
  • Property provides public getter/setter methods that can be serialized and deserialized.
  • Property has only public setter methods and no public getter methods, and can only be used for deserialization.
@slf4j public class JacksonTest {/** * javabeantojson */ @test public void testJavaBeanToJson() {WeChat = new  WeChat(); weChat.setId("zhuan2quan"); SetName (" Program new horizon "); weChat.setInterest(new String[]{"Java", "Spring Boot", "JVM"}); ObjectMapper mapper = new ObjectMapper(); try { String result = mapper.writeValueAsString(weChat); System.out.println(result); } catch (JsonProcessingException e) {log.error(" conversion exception ", e); }} / / @test public void testJsonToJavaBean() {String JSON = "{\ \" id ": \" zhuan2quan \ ", \ "name \" : \ \ "new horizon" program, \ "interest \" : [\ Java \ ", \ "Spring Boot \", \ "the JVM \]}"; ObjectMapper mapper = new ObjectMapper(); try { WeChat weChat = mapper.readValue(json, WeChat.class); System.out.println(weChat); } catch (JsonProcessingException e) {log.error(" parse exception ", e); } /** * JSON String to Map */ @test public void testJsonToMap() {String JSON = "{\ \" id ": \" zhuan2quan \ ", \ "name \" : \ \ "new horizon" program, \ "interest \" : [\ Java \ ", \ "Spring Boot \", \ "the JVM \]}"; ObjectMapper mapper = new ObjectMapper(); Try {// Deserialize the generic type. Use TypeReference to specify the deserialization type. Map<String, Object> map = mapper.readValue(json, new TypeReference<Map<String, Object>>() { }); System.out.println(map); } catch (JsonProcessingException e) {log.error(" parse exception ", e); */ @test public void testabeantoFile () {WeChat = new WeChat(); weChat.setId("zhuan2quan"); SetName (" Program new horizon "); weChat.setInterest(new String[]{"Java", "Spring Boot", "JVM"}); ObjectMapper mapper = new ObjectMapper(); Try {// Write to File mapper.writevalue (new File("/json.txt"), weChat); ReadValue (new File("/json.txt"), weChat1 = readValue(new File("/json.txt"), wechat.class); System.out.println(weChat1); } catch (IOException e) {log.error(" error ", e); } /** * javabeantoBytes */ @test public void testJavaBeanToBytes() {WeChat = new WeChat(); weChat.setId("zhuan2quan"); SetName (" Program new horizon "); weChat.setInterest(new String[]{"Java", "Spring Boot", "JVM"}); ObjectMapper mapper = new ObjectMapper(); Try {// Write to byte stream byte[] bytes = mapper. writeasBytes (weChat); WeChat1 = mapper.readValue(bytes, wechat.class); System.out.println(weChat1); } catch (IOException e) {log.error(" error ", e); }}}Copy the code

The code above uses Lombok annotations and unit test annotations, which can be replaced as needed.

JSON tree model

If the Json string is large, you can use the Json tree model to flexibly retrieve the desired field content. In Jackson, methods such as get, path, and HAS are provided to get or determine.

Let’s look at two examples directly:

@slf4j public class JacksonNodeTest {/** * JavaBean to JSON string */ @test public void testJsonNode() {// Build a JSON tree ObjectMapper mapper = new ObjectMapper(); ObjectNode root = mapper.createObjectNode(); root.put("id", "zhuan2quan"); Root. put("name", "program new view "); ArrayNode interest = root.putArray("interest"); interest.add("Java"); interest.add("Spring Boot"); interest.add("JVM"); JSON tree to JSON String String JSON = null; try { json = mapper.writeValueAsString(root); } catch (JsonProcessingException e) {log.error("Json Node conversion exception ", e); } System.out.println(json); } /** * parse JSON String into JSON tree */ @test public void testJsonToJsonNode() {String JSON = "{\ \" id ": \" zhuan2quan \ ", \ "name \" : \ \ "new horizon" program, \ "interest \" : [\ Java \ ", \ "Spring Boot \", \ "the JVM \]}"; ObjectMapper mapper = new ObjectMapper(); JsonNode JsonNode = mapper.readTree(JSON); String name = jsonNode.path("name").asText(); System.out.println(name); JsonNode interestNode = jsonNode.get("interest"); if (interestNode.isArray()){ for (JsonNode node : interestNode){ System.out.println(node.asText()); }}} catch (JsonProcessingException e) {log.error("Json Node conversion exception ", e); }}}Copy the code

The get method is similar to the path method except that if the key to be read does not exist in the Json string, the GET method is null, while the Path method returns the MissingNode instance object. In the case of the link method, no exception is thrown.

Streaming API

In addition to the above two forms, it is possible to operate based on the underlying streaming apis, mainly through the JsonGenerator and JsonParser apis, but the operation is more complicated and will not be demonstrated here.

Format unified Configuration

When using ObjectMapper, there are some fields that do not need to be serialized or deserialized in some cases, and you may need to specify formatted information, etc. In this case, you can use ObjectMapper to perform configuration.

/ / deserialization ignore json but Java objects don't exist in the existing property mapper. The configure (DeserializationFeature FAIL_ON_UNKNOWN_PROPERTIES, false); / / the serialization date format for the default yyyy - MM - dd 'T' HH: MM: ss. SSSZ mapper. The configure (SerializationFeature WRITE_DATES_AS_TIMESTAMPS, false); SetDateFormat (new SimpleDateFormat(" YYYY-MM-DD HH: MM :ss")); / / serialization to ignore the value of the attribute of a null mapper. SetSerializationInclusion (JsonInclude. Include. NON_NULL); / / serialization to ignore the value of the default value of attributes mapper. SetDefaultPropertyInclusion (JsonInclude. Include. NON_DEFAULT);Copy the code

For configuration items, an ObjectMapper implementation class JsonMapper is added in version 2.2. The function is consistent with ObjectMapper. However, a builder method has been added. You can configure it directly with the jsonMapper.Builder ().configure() method to obtain a JsonMapper object. Other methods of JsonMapper are basically integrated with ObjectMapper.

Use of annotations

The serialization and deserialization of the global format can be configured through unified configuration. However, in some individual scenarios, the configuration needs to be specific to the field, which requires annotations. For example, when a field in a Json string is inconsistent with a property in a Java object, annotations are needed to establish a direct relationship between them.

@jsonProperty, applied to a JavaBean field, specifies a field for the JSON mapping. By default, the mapped JSON field has the same name as the annotated field. You can specify the field name of the MAPPED JSON through the value attribute.

@jsonIgnore can be used on fields, getters/setters, and constructor arguments to specify that fields do not participate in serialization and deserialization.

@jsonIgnoreProperties applies to the class. When serialized, @jsonIgnoreProperties ({“prop1”, “prop2”}) ignores the pro1 and pro2 properties. @jsonIgnoreProperties (ignoreUnknown=true) ignores fields that do not exist in the class when deserializing.

@jsonFormat applies to fields and is usually used for formatting operations.

@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date date;
Copy the code

If the time field using JavaBean is JDK8 new time date (LocalDate/LocalTime/LocalDateTime) type, you need to add Jackson – datatype – jsr310 dependency. When it comes to dependencies, this is one of the dependencies SpringBoot introduces by default.

Of course, there are also some other annotations, such as @JsonPropertyOrder, @JsonRootName, @Jsonanysetter, @Jsonanygetter, @JsonNaming, etc. You can refer to the corresponding documents and examples when using them. I won’t list them all here.

Custom parsers

If the above annotations and unified configuration do not satisfy your requirements, you can customize the parser as shown in the following example:

public class MyFastjsonDeserialize extends JsonDeserializer<Point> { @Override public Point deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException { JsonNode node = jsonParser.getCodec().readTree(jsonParser); Iterator<JsonNode> iterator = node.get("coordinates").elements(); List<Double> list = new ArrayList<>(); while (iterator.hasNext()) { list.add(iterator.next().asDouble()); } return new Point(list.get(0), list.get(1)); }}Copy the code

Once the definition is complete, register it with Mapper:

ObjectMapper objectMapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(Point.class, new MyFastjsonDeserialize());
objectMapper.registerModule(module);
Copy the code

Jackson handle XML

Jackson also provides XML processing capabilities through the Jackson-dataformat-xml package. It is recommended to use the Woodstox-Core package when working with XML, which is an XML implementation that is more efficient and secure than the JDK’s native XML implementation.

<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
</dependency>
Copy the code

If you are using Java version 9 or more, may appear Java. Lang. NoClassDefFoundError: Javax.mail/XML/bind/JAXBException is unusual, this is because the Java 9 implements modular JDK, will originally and JDK packaged together JAXB implementations are separated. So you need to manually add JAXB’s implementation.

<dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
</dependency>
Copy the code

Here is a code example, which is basically very similar to the JSON API, XmlMapper is actually a subclass of ObjectMapper.

@Test public void testXml(){ WeChat weChat = new WeChat(); weChat.setId("zhuan2quan"); SetName (" Program new horizon "); weChat.setInterest(new String[]{"Java", "Spring Boot", "JVM"}); XmlMapper xmlMapper = new XmlMapper(); try { String xml = xmlMapper.writeValueAsString(weChat); System.out.println(xml); } catch (JsonProcessingException e) { e.printStackTrace(); }}Copy the code

After execution, output:

</name> <interest> <interest>Java</interest> <interest>Spring Boot</interest> <interest>JVM</interest> </interest> </WeChat>Copy the code

Integration in Spring Boot

In the beginning, we have seen that Spring Boot has introduced Jackson dependencies by default, and is also using us for additional operations. Jackson is already using Jackson to bind jSON-formatted data to MVC parameters.

If the default configuration of Spring Boot is not suitable for project requirements, you can also use the built-in configuration. Take the application. Yml configuration as an example, you can configure the corresponding options by specifying the following properties:

Date format: yyyY-MM-DD HH: MM :ss Or specific format fully qualified name of a class of spring. Jackson. If the date format - # open Jackson deserialization spring. Jackson. Deserialization # if the generators to json. Spring.jackson. generator # specify the format of Joda date/time, such as YYYY-MM-DDHh: MM :ss). If it's not configured, Locale.spring.jackson. Locale # specifies whether to enable Jackson common features for json. Mapper # Whether to enable Jackson parser spring.jackson.parser PropertyNamingStrategy(CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES) or the fully qualified class name of the PropertyNamingStrategy subclass. Spring. Jackson. The property - naming strategy # if the serialization to Jackson. Spring. Jackson. The serialization # specified serialization attribute way of inclusion, Specific see JsonInclude. Include the enumeration. Spring. Jackson. The serialization - inclusion # specified date format time zones, Such as America/Los_Angeles or GMT+ 10.spring.jackson. time-zoneCopy the code

Spring Boot auto-configuration is very convenient, but at some point you need to manually configure beans instead of automatically configuring beans. The configuration can be performed in the following format:

@Configuration public class JacksonConfig { @Bean @Qualifier("json") public ObjectMapper jsonMapper(Jackson2ObjectMapperBuilder builder) { ObjectMapper mapper = builder.createXmlMapper(false) .build(); mapper.enable(SerializationFeature.INDENT_OUTPUT); mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); return mapper; }}Copy the code

When the configuration is complete, you can directly inject it in the place where it is used:

@Resource
private ObjectMapper jsonMapper;
Copy the code

For the above injection, some friends may ask, is there a thread safety problem? Don’t worry about this ObjectMapper being thread-safe.

summary

After the explanation of this article, you should have a comprehensive understanding of Jackson. On a personal level, it was fun to learn about Jackson.