Use of Jackson's ObjectMapper object

Keywords: jackson

Statement:

This blog is reproduced from
https://blog.csdn.net/blwinner/article/details/99942211 It is designed to facilitate review and review and is not used for commercial purposes.

The source of this blog has been indicated. If there is infringement, please inform and delete it immediately.

1. Introduction

The Jackson ObjectMapper class (com.fasterxml.jackson.databind.ObjectMapper) is the easiest way to parse JSON using Jackson. Jackson ObjectMapper can parse JSON from strings, streams, or files, and create Java objects or object graphs to represent the parsed JSON. Parsing JSON to Java objects is also called deserializing Java objects from JSON
Jackson ObjectMapper can also create JSON from Java objects. The process of generating JSON from Java objects is also called serializing Java objects to JSON
The Jackson object mapper can parse JSON into user-defined class objects or into objects of JSON's built-in tree model (see below for details)

2. Jackson's data binding

The ObjectMapper object definition is located in the Jackson Databind project, so relevant project paths or dependencies should be added to the application

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-core</artifactId>
  <version>2.9.6</version>
</dependency>

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-annotations</artifactId>
  <version>2.9.6</version>
</dependency>

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.9.6</version>
</dependency>

3. Jackson ObjectMapper object example

A simple example:

public class Car {
    private String brand = null;
    private int doors = 0;

    public String getBrand() { return this.brand; }
    public void   setBrand(String brand){ this.brand = brand;}

    public int  getDoors() { return this.doors; }
    public void setDoors (int doors) { this.doors = doors; }
}
...........................................................
ObjectMapper objectMapper = new ObjectMapper();
String carJson = "{ \"brand\" : \"Mercedes\", \"doors\" : 5 }";
try {
    Car car = objectMapper.readValue(carJson, Car.class);
    System.out.println("car brand = " + car.getBrand());
    System.out.println("car doors = " + car.getDoors());
} catch (IOException e) {
    e.printStackTrace();
}

As shown in the example, the first parameter of the readValue() method is the JSON data source (string, stream or file), and the second parameter is to parse the target Java class. Car.class is passed in here

4. Deserialization

4.1 ObjectMapper's process from JSON attribute matching to Java attribute matching

To correctly read Java objects from JSON, it is important to understand how Jackson maps from JSON objects to Java objects
By default, Jackson maps a property of a JSON object to a Java object. He uses the name of the JSON property to find the matching getter/setter method in the Java object. Jackson removes the get/set character part of the getter/setter method name and lowercases the first character of the remaining characters of the method name to get the JSON property name
In the example in the previous section, the name of the JSON attribute is brand, which matches the getter/setter method named getBrand()/setBrand() in the Java class. The name of the JSON attribute is engineNumber, which matches getEngineNumber()/setEngineNumber()
If you want to use other methods to match JSON object properties and Java object properties, you can customize the serialization / deserialization process, or use other methods Jackson notes

4.2 reading Java objects from JSON strings

Reading a Java object from a JSON string is very simple. The previous example has shown the specific process. The JSON string is passed to the ObjectMapper.readValue() method as the first parameter. See the previous example

4.3 reading Java objects from JSON Reader objects

You can also read a Java object from JSON through the Reader instance

ObjectMapper objectMapper = new ObjectMapper();
String carJson = "{ \"brand\" : \"Mercedes\", \"doors\" : 4 }";
Reader reader = new StringReader(carJson);
Car car = objectMapper.readValue(reader, Car.class);

4.4 reading Java objects from JSON files

You can also read JSON from through the FileReader instance (just replace the StringReader in the above example). Of course, you can also directly use the File object

ObjectMapper objectMapper = new ObjectMapper();
File file = new File("data/car.json");
Car car = objectMapper.readValue(file, Car.class);

4.5 get JSON data from URL and read Java object

You can read Java objects after obtaining JSON data through the URL(java.net.URL)

ObjectMapper objectMapper = new ObjectMapper();
URL url = new URL("file:data/car.json");
Car car = objectMapper.readValue(url, Car.class);

This example uses a file URL. Of course, you can also use an HTTP URL

4.6 get JSON data from Java InputStream and read Java objects

ObjectMapper objectMapper = new ObjectMapper();
InputStream input = new FileInputStream("data/car.json");
Car car = objectMapper.readValue(input, Car.class);

4.7 get JSON data from byte array and read Java objects

ObjectMapper objectMapper = new ObjectMapper();
String carJson = "{ \"brand\" : \"Mercedes\", \"doors\" : 5 }";
byte[] bytes = carJson.getBytes("UTF-8");
Car car = objectMapper.readValue(bytes, Car.class);

4.8 reading Java object array from JSON array string

ObjectMapper can also read a set of Java objects from JSON array strings

String jsonArray = "[{\"brand\":\"ford\"}, {\"brand\":\"Fiat\"}]";
ObjectMapper objectMapper = new ObjectMapper();
Car[] cars2 = objectMapper.readValue(jsonArray, Car[].class);

Notice how the Car class array is passed in as the second parameter of the readValue() method, telling ObjectMapper that it expects to read a set of Car instances from JSON
Of course, JSON sources are not only strings, but also files, URLs, inputstreams, readers, and so on

4.9 reading Java List objects from JSON array strings

String jsonArray = "[{\"brand\":\"ford\"}, {\"brand\":\"Fiat\"}]";
ObjectMapper objectMapper = new ObjectMapper();
List<Car> cars1 = objectMapper.readValue(jsonArray, new TypeReference<List<Car>>(){});

Note the TypeReference type parameter passed to readValue(). This parameter tells Jackson to read a "column" of Car objects

  • Jackson generates Java objects through reflection, but the template will erase the type, so it is wrapped with TypeReference here

4.10 reading Java Map objects from JSON strings

ObjectMapper can read a Java Map from the JSON string, which is very useful when you don't know the format of the JSON to be extracted. It usually reads the JSON object into a JavaMap object. The properties of each JSON object will become key value pairs in the JavaMap

String jsonObject = "{\"brand\":\"ford\", \"doors\":5}";
ObjectMapper objectMapper = new ObjectMapper();
Map<String, Object> jsonMap = objectMapper.readValue(jsonObject, new TypeReference<Map<String,Object>>(){});

4.11 special circumstances

4.11.1 ignore JSON attributes that Java objects do not have

Sometimes the properties of JSON data you want to read are more than those of your Java object. By default, Jackson will throw an exception, which means that the unknown property XXX cannot be found in the Java object
However, sometimes we need to allow more JSON attributes than the attributes of the Java object to be generated. For example, you want to obtain JSON data from a REST service, but it contains much more content than you need. This is that you can ignore those redundant attributes by configuring Jackson's Feature

objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

Note: you can also use the ObjectMapper.enabled()/disabled() method for configuration

4.11.2 the JSON attribute value is NULL and corresponds to the exception generated by the Java Native type

The Feature of ObjectMapper can be configured so that when the property value contained in the JSON string is null and the property of the Java object corresponding to the property is a primitive type (primitive type: int, long, float, double, etc.), the deserialization fails and an exception is thrown
Modify the definition of Car class:

public class Car {
    private String brand = null;
    private int doors = 0;

    public String getBrand() { return this.brand; }
    public void   setBrand(String brand){ this.brand = brand;}

    public int  getDoors(){ return this.doors; }
    public void setDoors (int doors) { this.doors = doors; }
}

Note that the type of the property doors is Java Native type int (not object)
Now suppose there is a JSON string to match the Car class:

{ "brand":"Toyota", "doors":null }

Note that the value of the property doors is null
The value of the native data type in Java cannot be null. Therefore, ObjectMapper will ignore the property of the native type of null value by default. However, you can also configure the Feature of ObjectMapper to throw an exception, as follows:

ObjectMapper objectMapper = new ObjectMapper();

objectMapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, true);

By setting fail_ ON_ NULL_ FOR_ If the primitives property is true, you will throw an exception when trying to resolve the JSON property of null value to a Java Native type property, as follows:

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, true);
String carJson = "{ \"brand\":\"Toyota\", \"doors\":null }";
Car car = objectMapper.readValue(carJson, Car.class);

The exception information thrown is as follows:

Exception in thread "main" com.fasterxml.jackson.databind.exc.MismatchedInputException:
    Cannot map `null` into type int
    (set DeserializationConfig.DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES to 'false' to allow)
 at [Source: (String)
    "{ "brand":"Toyota", "doors":null }"; line: 1, column: 29] (through reference chain: jackson.Car["doors"])

4.12 custom deserialization process

Sometimes, we need to deserialize JSON strings to a Java object in a way different from ObjectMapper's default. At this time, we can add a custom deserializer to ObjectMapper so that it can be deserialized in the way you set
The following is an example of registering and using a custom deserializer with ObjectMapper:

String json = "{ \"brand\" : \"Ford\", \"doors\" : 6 }";
//Define a model that uses' CarDeserializer 'as the deserializer
SimpleModule module = new SimpleModule("CarDeserializer", new Version(3, 1, 8, null, null, null));
//Specifies the Java class (Car class) that the deserializer acts on
module.addDeserializer(Car.class, new CarDeserializer(Car.class));

ObjectMapper mapper = new ObjectMapper();
//Add a serialized / deserialized model for ObjectMapper
mapper.registerModule(module);
//Deserialize Car class
Car car = mapper.readValue(json, Car.class);

Here is the definition of the deserializer CarDeserializer:

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;

import java.io.IOException;

public class CarDeserializer extends StdDeserializer<Car> {

    public CarDeserializer(Class<?> vc) {
        super(vc);
    }

    @Override
    public Car deserialize(JsonParser parser, DeserializationContext deserializer) throws IOException {
        Car car = new Car();
        while(!parser.isClosed()){
            JsonToken jsonToken = parser.nextToken();

            if(JsonToken.FIELD_NAME.equals(jsonToken)){
                String fieldName = parser.getCurrentName();
                System.out.println(fieldName);

                jsonToken = parser.nextToken();

                if("brand".equals(fieldName)){
                    car.setBrand(parser.getValueAsString());
                } else if ("doors".equals(fieldName)){
                    car.setDoors(parser.getValueAsInt());
                }
            }
        }
        return car;
    }
}

5. Serialization

5.1 serialize objects into JSON

ObjectMapper instance can also be used to generate JSON data from an object. The following methods can be used:

  • writeValue()
  • writeValueAsString()
  • writeValueAsBytes()

The following is an example of serializing a Car object into JSON:

ObjectMapper objectMapper = new ObjectMapper();

Car car = new Car();
car.brand = "BMW";
car.doors = 4;

objectMapper.writeValue(new FileOutputStream("data/output-2.json"), car);

Here we first create a ObjectMapper instance and a Car instance, then call the writeValue() method of ObjectMapper to convert the Car instance to JSON and output it to FileOutputStream.
writeValueAsString() and writeValueAsBytes() can also generate JSON from objects and return JSON of a String or Byte array, as follows:

ObjectMapper objectMapper = new ObjectMapper();

Car car = new Car();
car.brand = "BMW";
car.doors = 4;

String json = objectMapper.writeValueAsString(car);
System.out.println(json);

The output is:

{"brand":"BMW","doors":4}

5.2 custom serialization process

Sometimes you don't want to use Jackson's default serialization process to turn a Java object into JSON. For example, you may use different attribute names from Java objects in JSON, or you may want to ignore some attributes completely
Jackson can set up a custom serializer for ObjectMapper, which will be registered to serialize an actual class, and then called when ObjectMapper performs serialization, such as serializing Car objects
The following example shows how to register a custom serializer for the Car class:

//Initializing serializer with Car class 
CarSerializer carSerializer = new CarSerializer(Car.class);
ObjectMapper objectMapper = new ObjectMapper();
//Create a new serialization model, and the serializer uses the CarSerializer class
SimpleModule module = new SimpleModule("CarSerializer", new Version(2, 1, 3, null, null, null));
//Register the carSerializer to serialize the Car
module.addSerializer(Car.class, carSerializer);

objectMapper.registerModule(module);

Car car = new Car();
car.setBrand("Mercedes");
car.setDoors(5);

String carJson = objectMapper.writeValueAsString(car);

The output is:

{"producer":"Mercedes","doorCount":5}

The following is the CarSerializer class definition:

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;

import java.io.IOException;

public class CarSerializer extends StdSerializer<Car> {

    protected CarSerializer(Class<Car> t) {
        super(t);
    }
	@override
    public void serialize(Car car, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
            throws IOException {
        jsonGenerator.writeStartObject();
        jsonGenerator.writeStringField("producer", car.getBrand());
        jsonGenerator.writeNumberField("doorCount", car.getDoors());
        jsonGenerator.writeEndObject();
    }
}

Note: the second parameter of serialize is a JsonGenerator instance, which you can use to serialize an object, here is the Car object

6. Jackson's date formatting

By default, Jackson will serialize a java.util.Date object into a long value, that is, the number of milliseconds from 1970-01-1 to the present. Of course, Jackson also supports formatting dates into strings

6.1 Date to long

First, take a look at Jackson's default process of serializing Date into long. The following is a Java class containing Date type attributes:

public class Transaction {
    private String type = null;
    private Date date = null;

    public Transaction() {
    }

    public Transaction(String type, Date date) {
        this.type = type;
        this.date = date;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }
}

The process of serializing Transaction class objects with ObjectMapper is the same as other Java objects:

Transaction transaction = new Transaction("transfer", new Date());

ObjectMapper objectMapper = new ObjectMapper();
String output = objectMapper.writeValueAsString(transaction);

System.out.println(output);

Serialization result:

{"type":"transfer","date":1516442298301}

The attribute date is serialized as a long integer

6.2 Date to String

long serialization has poor readability, so Jackson provides date serialization in text format. You can specify a SimpleDateFormat instance for ObjectMapper to extract Jackson date in format

SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
objectMapper2.setDateFormat(dateFormat);

String output2 = objectMapper2.writeValueAsString(transaction);
System.out.println(output2);

Serialization result:

{"type":"transfer","date":"2018-01-20"}

As you can see, the attribute date is formatted as a string

7. Jackson's tree model

Jackson has a built-in tree model that can be used to represent a JSON object. This tree model is very useful. For example, you don't know the structure of the received JSON data, or you don't want to create a new Java class to represent the JSON data, or you want to operate on it before using or forwarding JSON data
Jackson's tree model is implemented by the JsonNode class. You can use the ObjectMapper instance to parse JSON into the JsonNode model, just like deserializing a custom class object
The following example demonstrates the usage of JsonNode

7.1 Jackson tree model example

String carJson = "{ \"brand\" : \"Mercedes\", \"doors\" : 5 }";
ObjectMapper objectMapper = new ObjectMapper();
try {
    JsonNode jsonNode = objectMapper.readValue(carJson, JsonNode.class);
} catch (IOException e) {
    e.printStackTrace();
}

Here, we use JsonNode.class instead of Car.class to parse JSON strings
ObjectMapper provides a simpler method to get JsonNode: readtree(). This method returns a JsonNode, as follows:

String carJson = "{ \"brand\" : \"Mercedes\", \"doors\" : 5 }";
ObjectMapper objectMapper = new ObjectMapper();
try {
    JsonNode jsonNode = objectMapper.readTree(carJson);
} catch (IOException e) {
    e.printStackTrace();
}

7.2 JsonNode class

JsonNode provides a very flexible and dynamic access method, which can navigate and browse JSON like accessing Java objects
If JSON is parsed into a JsonNode instance (or a JsonNode instance tree), you can browse the JsonNode tree model. In the following example, JsonNode is used to access properties, arrays, objects, etc. in JSON:

String carJson =
        "{ \"brand\" : \"Mercedes\", \"doors\" : 5," +
        "  \"owners\" : [\"John\", \"Jack\", \"Jill\"]," +
        "  \"nestedObject\" : { \"field\" : \"value\" } }";
ObjectMapper objectMapper = new ObjectMapper();
try {
    JsonNode jsonNode = objectMapper.readValue(carJson, JsonNode.class);

    JsonNode brandNode = jsonNode.get("brand");
    String brand = brandNode.asText();
    System.out.println("brand = " + brand);

    JsonNode doorsNode = jsonNode.get("doors");
    int doors = doorsNode.asInt();
    System.out.println("doors = " + doors);

    JsonNode array = jsonNode.get("owners");
    JsonNode jsonNode = array.get(0);
    String john = jsonNode.asText();
    System.out.println("john  = " + john);

    JsonNode child = jsonNode.get("nestedObject");
    JsonNode childField = child.get("field");
    String field = childField.asText();
    System.out.println("field = " + field);

} catch (IOException e) {
    e.printStackTrace();
}

The above JSON contains an array attribute named owner and an object attribute named nestedObject
Whether you are accessing an attribute, array or embedded object, you can use the get() method of JsonNode. You can access an attribute of a JsonNode instance by passing in a string for get(). Passing in an index is to access the array represented by the JsonNode instance, and the index represents the position of the array element you want to access

Posted by jl5501 on Wed, 27 Oct 2021 03:31:59 -0700