An overview of

The previous article introduced the ObjectMapper in Jackson and how to use it to serialize and deserialize between Json and Java objects, and concluded with some of the advanced serialization/deserialization features in Jackson. In this article, we’ll look at some of the common (serialization/deserialization) annotations in Jackson and show examples of how to use them to improve our productivity with Json.


Serialized annotations

@JsonAnyGetter

The @JsonanyGetter annotation allows flexibility in using mapping (key-value pairs, such as Map) fields as standard properties.

We declare the following Java classes:

@Data
@Accessors(chain = true)
public static class ExtendableBean {
    public String name;
    private Map<String, String> properties;

    @JsonAnyGetter
    public Map<String, String> getProperties(a) {
        returnproperties; }}Copy the code

Write test code to test @jsonanygetter:

@Test
public void testJsonAnyGetter(a) throws JsonProcessingException {
    ExtendableBean extendableBean = new ExtendableBean();
    Map<String, String> map = new HashMap<>();
    map.put("age"."13");
    extendableBean.setName("dxsn").setProperties(map);
    log.info(new ObjectMapper().writeValueAsString(extendableBean));
  	// Print: {"name":" DXSN ","age":"13"}
    assertThat(new ObjectMapper().writeValueAsString(extendableBean)).contains("name");
    assertThat(new ObjectMapper().writeValueAsString(extendableBean)).contains("age");
}
Copy the code

As shown above, you can see that the key-value pair (Map) in the Properties property is extended to the Json object of the ExtendableBean.

@JsonGetter

The @JsonGetter annotation is an alternative to the @JsonProperty annotation to mark a method as a getter.

We create the following Java class

@Data
@AllArgsConstructor
@NoArgsConstructor
public static class MyBean {
    public int id;
    private String name;

    @JsonGetter("name")
    public String getTheName(a) {
        returnname; }}Copy the code

As shown above, we declare a getTheName() method in the class and decorate it with @jsongetter (“name”), which Jackson will recognize as the get method for the name property.

Write test code:

@Test
public void testJsonGetter(a) throws JsonProcessingException {
    MyBean myBean = new MyBean(1."dxsn");
    String jsonStr = new ObjectMapper().writeValueAsString(myBean);
    log.info(jsonStr);
    assertThat(jsonStr).contains("id");
    assertThat(jsonStr).contains("name");
}
Copy the code

As you can see, Jackson has also serialized the private property name.

@JsonPropertyOrder

We can use the @JsonPropertyOrder annotation to specify the serialization order of Java objects’ properties.

@JsonPropertyOrder({"name"."id"})
//order by key's name
//@JsonPropertyOrder(alphabetic = true)
@Data
@Accessors(chain = true)
public static class MyOrderBean {
  public int id;
  public String name;
}
Copy the code

Write test code:

@Test
public void testJsonPropertyOrder1(a) throws JsonProcessingException {
    MyOrderBean myOrderBean = new MyOrderBean().setId(1).setName("dxsn");
    String jsonStr = new ObjectMapper().writeValueAsString(myOrderBean);
    log.info(jsonStr);
    assertThat(jsonStr).isEqualTo("{\"name\":\"dxsn\",\"id\":1}");
}
Copy the code

As you can see above, the order of the properties in the serialized Json object is exactly the order we specified in the annotations.

@JsonRawValue

The @jsonRawValue annotation instructs Jackson to serialize attributes as-is.

In the following example, we use @jsonRawValue to embed some custom JSON as an entity value:

@Data
@AllArgsConstructor
@NoArgsConstructor
public static class RawBean {
    public String name;
    @JsonRawValue
    public String json;
}
Copy the code

Write test code:

@Test
public void testJsonRawValue(a) throws JsonProcessingException {
    RawBean rawBean = new RawBean("dxsn"."{\"love\":\"true\"}");
    log.info(new ObjectMapper().writeValueAsString(rawBean));
  	// Output :{" name":" DXSN ","json":{"love":"true"}}
    String result = new ObjectMapper().writeValueAsString(rawBean);
    assertThat(result).contains("dxsn");
    assertThat(result).contains("{\"love\":\"true\"}");
}
Copy the code

@JsonValue

@jsonValue indicates that Jackson will use a method to serialize the entire instance.

Let’s create an enumeration class:

@AllArgsConstructor
public static enum TypeEnumWithValue {
    TYPE1(1."Type A"), TYPE2(2."Type 2");
    private Integer id;
    private String name;

    @JsonValue
    public String getName(a) {
        returnname; }}Copy the code

As above, we decorate getName() with @jsonValue.

Write test code:

@Test
public void testJsonValue(a) throws JsonProcessingException {
    String  jsonStr = new ObjectMapper().writeValueAsString(TypeEnumWithValue.TYPE2);
    log.info(jsonStr);
    assertThat(jsonStr).isEqualTo("Type 2");
}
Copy the code

As you can see, the serialized value of the object of the enumerated class is the return value of the getName() method.

@JsonRootName

If wrapping is enabled, use the @jsonRootName annotation to specify the name of the root wrapper to use.

Let’s create a Java class decorated with @jsonRootName:

@JsonRootName(value = "user")
@Data
@AllArgsConstructor
public static class UserWithRoot {
    public int id;
    public String name;
}
Copy the code

Writing tests:

@Test
public void testJsonRootName(a) throws JsonProcessingException {
    UserWithRoot userWithRoot = new UserWithRoot(1."dxsn");
    ObjectMapper mapper = new ObjectMapper();
  	//⬇️ key!!
    mapper.enable(SerializationFeature.WRAP_ROOT_VALUE);
  
    String result = mapper.writeValueAsString(userWithRoot);
    log.info(result);
  	{"user":{"id":1,"name":" DXSN "}}
    assertThat(result).contains("dxsn");
    assertThat(result).contains("user");
}
Copy the code

The above code, we through the open ObjectMapper SerializationFeature. WRAP_ROOT_VALUE. You can see that the serialized Json object of the UserWithRoot object is wrapped in user instead of {“id”:1,”name”:” DXSN “}.

@JsonSerialize

The @jsonSerialize annotation represents a custom serializer to use when serializing entities.

We define a custom serializer:

public static class CustomDateSerializer extends StdSerializer<Date> {
    private static SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");

    public CustomDateSerializer(a) {
        this(null);
    }

    public CustomDateSerializer(Class<Date> t) {
        super(t);
    }

    @Override
    public void serialize( Date value, JsonGenerator gen, SerializerProvider arg2) throws IOException, JsonProcessingException { gen.writeString(formatter.format(value)); }}Copy the code

Using a custom serializer, create a Java class:

@Data
@AllArgsConstructor
public static class Event {
    public String name;
    @JsonSerialize(using = CustomDateSerializer.class)
    public Date eventDate;
}
Copy the code

Write test code:

@Test
public void testJsonSerialize(a) throws ParseException, JsonProcessingException {
    SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
    String toParse = "The 20-12-2014 02:30:00";
    Date date = formatter.parse(toParse);
    Event event = new Event("party", date);
    String result = new ObjectMapper().writeValueAsString(event);
    assertThat(result).contains(toParse);
}
Copy the code

As you can see, after modifying a specified property with the @jsonSerialize annotation, the property will be serialized using the specified serializer.


Deserialized annotations

@JsonCreator

We can use the @jsonCreator annotation to optimize/replace the constructor/factory used in deserialization.

This is useful when we need to deserialize some JSON that doesn’t exactly match the target entity we need to fetch.

Now, we have the following Json object:

{"id":1."theName":"My bean"}
Copy the code

We have a Java class:

@Data
public static class BeanWithCreator {
    private int id;
    private String name;
}
Copy the code

At this point, there is no theName field in our target entity, only theName field. For now, we don’t want to change the entity itself, so we can decorate the constructor by using @jsonCreator and @JsonProperty annotations:

@Data
public static class BeanWithCreator {
    private int id;
    private String name;

    @JsonCreator
    public BeanWithCreator(@JsonProperty("id") int id, @JsonProperty("theName") String name) {
        this.id = id;
        this.name = name; }}Copy the code

Writing tests:

@Test
public void beanWithCreatorTest(a) throws JsonProcessingException {
    String str = "{\"id\":1,\"theName\":\"My bean\"}";
    BeanWithCreator bean = new ObjectMapper()
        .readerFor(BeanWithCreator.class)
        .readValue(str);
 	 	assertThat(bean.getId()).isEqualTo(1);
    assertThat(bean.getName()).isEqualTo("My bean");
}
Copy the code

As you can see, even though the field names in the Json object are different from those in the entity class, deserialization succeeded because we manually specified the names of the mapped fields.

@JacksonInject

@JacksonInject indicates that properties in Java objects will be assigned by injection rather than getting their values from JSON data.

Create the following entity class with fields decorated by @JacksonInject:

public static class BeanWithInject {
    @JacksonInject
    public int id;
    public String name;
}
Copy the code

Writing tests:

@Test
public void jacksonInjectTest(a) throws JsonProcessingException {
    String json = "{\"name\":\"dxsn\"}";
    InjectableValues inject = new InjectableValues.Std()
        .addValue(int.class, 1);
    BeanWithInject bean = new ObjectMapper().reader(inject)
        .forType(BeanWithInject.class)
        .readValue(json);
    assertThat(bean.id).isEqualTo(1);
    assertThat(bean.name).isEqualTo("dxsn");
}
Copy the code

As shown above, we deserialized the JSON string (with only the Name field) in our test, where id is assigned to the property by injection.

@JsonAnySetter

The @jsonanysetter allows us the flexibility to use mappings (key-value pairs, Maps) as standard properties. At deserialization, JSON properties are added to the map.

Create an entity class with @jsonanysetter:

public static class ExtendableBean {
    public String name;
    public Map<String, String> properties;

    @JsonAnySetter
    public void add(String key, String value) {
        if (properties == null) {
            properties = newHashMap<>(); } properties.put(key, value); }}Copy the code

Writing tests:

@Test
public void testJsonAnySetter(a) throws JsonProcessingException {
    String json = "{\"name\":\"dxsn\", \"attr2\":\"val2\", \"attr1\":\"val1\"}";
    ExtendableBean extendableBean = new ObjectMapper().readerFor(ExtendableBean.class).readValue(json);
    assertThat(extendableBean.name).isEqualTo("dxsn");
    assertThat(extendableBean.properties.size()).isEqualTo(2);
}
Copy the code

As you can see, the attr1, attr2 properties in the JSON object go to Properties after deserialization.

@JsonSetter

@jsonSetter is an alternative method to @JsonProperty that marks the method as a setter method for the property. This is useful when we need to read some JSON data, but the target entity class doesn’t exactly match that data, so we need to optimize it to fit that data.

Create the following entity class:

@Data
public static class MyBean {
    public int id;
    private String name;

    @JsonSetter("name")
    public void setTheName(String name) {
        this.name = "hello "+ name; }}Copy the code

Writing tests:

@Test
public void testJsonSetter(a) throws JsonProcessingException {
    String json = "{\"id\":1,\"name\":\"dxsn\"}";
    MyBean bean = new ObjectMapper().readerFor(MyBean.class).readValue(json);
    assertThat(bean.getName()).isEqualTo("hello dxsn");
}
Copy the code

As you can see, the name attribute in the JSON object is “DXSN”. We have defined the @jsonsetter (“name”) annotation in the MyBean class to modify the method. This indicates that the name attribute will be removed from the method when the object of this class is unordered. Finally, the value of name in the MyBean object changes to Hello DXSN.

@JsonDeserialize

The @jsondeserialize annotation specifies the deserializer to use when deserializing.

A custom deserializer is defined as follows:

public static class CustomDateDeserializer extends StdDeserializer<Date> {
    private static SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");

    public CustomDateDeserializer(a) {
        this(null);
    }

    public CustomDateDeserializer(Class
        vc) {
        super(vc);
    }

    @Override
    public Date deserialize(JsonParser jsonparser, DeserializationContext context) throws IOException {
        String date = jsonparser.getText();
        try {
            return formatter.parse(date);
        } catch (ParseException e) {
            throw newRuntimeException(e); }}}Copy the code

Create a use @ JsonDeserialize (using = CustomDateDeserializer. Class) modified entity class:

public static class Event {
    public String name;
    @JsonDeserialize(using = CustomDateDeserializer.class)
    public Date eventDate;
}
Copy the code

Writing tests:

@Test
public void whenDeserializingUsingJsonDeserialize_thenCorrect(a)
    throws IOException {
    String json = "{\"name\":\"party\",\"eventDate\":\"20-12-2014 02:30:00\"}";
    SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
    Event event = new ObjectMapper().readerFor(Event.class).readValue(json);
    assertThat(event.name).isEqualTo("party");
    assertThat(event.eventDate).isEqualTo(df.format(event.eventDate));

}
Copy the code

As you can see, in the Event object, the eventDate property deserializes “20-12-2014 02:30:00” into a Date object using a custom deserializer.

@JsonAlias

@jsonAlias defines one or more alternative names for attributes during deserialization. Let’s look at a simple example of how this annotation works:

@Data
public static class AliasBean {
    @JsonAlias({"fName"."f_name"})
    private String firstName;
    private String lastName;
}
Copy the code

As above, we wrote an AliasBean entity class decorated with @JsonAlias.

Writing tests:

@Test
public void whenDeserializingUsingJsonAlias_thenCorrect(a) throws IOException {
    String json = "{\"fName\": \"John\", \"lastName\": \"Green\"}";
    AliasBean aliasBean = new ObjectMapper().readerFor(AliasBean.class).readValue(json);
    assertThat(aliasBean.getFirstName()).isEqualTo("John");
}
Copy the code

As you can see, even though the field name in the JSON object is fName, the firstName property is decorated with @jsonAlias in the AliasBean and two aliases are specified. So after deserialization, fName is mapped to the firstName property of the AliasBean object.


More and more

In addition to the above notes, Jackson also provides many additional notes, which are not listed here, but some commonly used ones are listed below:

  • @jsonProperty: You can add the @jsonProperty annotation to the specified property of the class to indicate its corresponding property name in JSON.
  • JsonFormat: This annotation can specify a string format output when serializing date/time type attributes in an object, such as: @jsonformat (shape = jsonformat.shape. STRING, pattern = “DD-MM-YYYY HH: MM: SS”).
  • @jsonunwrapped: @jsonunwrapped defines values that should be flattened when serializing/deserializing.
  • @jsonIgnore: Ignores embellished attributes when serializing/deserializing.
  • .

conclusion

This article mainly introduces the serialization/deserialization annotations commonly used by Jackson, and finally introduces several common annotations commonly used by Jackson. There are many more annotations provided in Jackson that make serialization/deserialization easier. If you want to replace a library with Jackson, hopefully this article will help.

The code address involved in this article: gitee.com/jeker8chen/…


🌟 🌟 🌟 🌟 🌟 🌟 🌟 🌟 🌟 🌟 🌟 🌟 🌟 🌟 🌟 🌟 🌟 🌟

Welcome to my blog:blog.dongxishaonian.tech

Pay attention to the author’s public account, push all kinds of original/high quality technical articles ⬇️