Jackjson usage and resolve deserialization of nested quoted string objects

environment

java:1.8+
jackson-databind:2.8.11

preface

Recently, when making requirements, the parsed json is strange. There are multiple layers of json nested inside, and there will be single quotation marks in these nested json. After using Java to store into the database, there will be escape characters. In this way, an error will be reported during deserialization and the parsing fails.
At first, I used fastjason to deserialize, but the support for single quotation marks and escape characters is really not good enough.
Later, jackson is used instead. It is convenient for escape characters, single quotation marks, null, empty strings and so on. It has a very good solution.

Introduce dependency

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

usage method

Create objectMapper

In the first step, we need to create an ObjectMapper object through which we can serialize or deserialize.

ObjectMapper objectMapper = new ObjectMapper();

Simple use of serialization and deserialization

For simple parsing:

User user = new User();
user.setName("rongK");
user.setAge(28);
user.setCreateTime(new Timestamp(new Date().getTime()));
//serialize
String s = objectMapper.writeValueAsString(user);
System.out.println("Object serialization:" + s);// Object serialization: {name":"rongK","age":28,"createTime":1598081001761}
//Deserialization
User u = objectMapper.readValue(s, User.class);
System.out.println("Object deserialization:" + u);// Object deserialization: user (name = longk, age = 28, createtime = 2020-08-22 15:23:21.761)

The above example is simple to use, but there are some situations as follows:

① There is a field in the deserialized json, which is not in the POJO class
② There are some problems with the deserialized json format:

{
	//User is a POJO, not a string
	//By default, deserialization fails
	"User": ""
}

③ There are single quotes in deserialization json:

{"expression":"${detail.putDetailJsonParam(execution,ruleService.mergeFilterKey(null,null,jsonUtils.parseObject('{"orderId":'.concat(orderId).concat('}')),'preposition','sgy',jsonUtils.parseJson2Array('["info","attributeJSON"]','java.lang.String')).infoJson)}"

It can be seen that value is a string, but there are nested use of single quotation marks and double quotation marks in the string.

④ There are escape characters or special characters;

For use problems, Jackjson can help us solve them through some configurations;

Ignore unmapped fields

When we deserialize, there are often fields in the class that do not exist in json, such as:
There is a Properties class in which only two fields are defined:

@Data
public class Properties {
    private Document document;
    private String name;
}

However, there are fields in our json string that are not defined by the Properties class:

{
   "properties": {
      "defaultflow":false,
      "executionlisteners":"",
      "documentation":"",
      "overrideid":"",
      "name":"test"
  }
}

At this time, we need to ignore the mapped fields

Method 1:

@JsonIgnoreProperties(ignoreUnknown = true)

In the POJO class,
Disadvantages: POJO classes combined in POJO also need to be added

Method 2:

Recommended use

objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

Single quotation mark solution

//Single quotation marks are allowed
objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true) ;

Solutions to null and empty strings

//null and empty strings are supported
        objectMapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);

It should be noted here that the following scenarios can not solve the problem even if the above settings are made

{
	//User is a POJO, not a string
	//By default, deserialization fails
	"User": ""
}

Methods to solve escape characters and special fields

//Special characters and escape characters are allowed
objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true) ;

Resolve deserialization of nested string objects with Jackson (quoted)

The assumptions are as follows

{
    "id": "abcd1234",
    "name": "test",
    "connections": {
        "default": "http://foo.com/api/",
        "dev": "http://dev.foo.com/api/v2"
    },
    "document": "",//document is essentially a class with two fields
    "settings": {
        "foo": "{\n \"fooId\": 1, \"token\": \"abc\"}",
        "bar": "{\"barId\": 2, \"accountId\": \"d7cj3\"}"
    }
}
@Data
public class Documentation {

    private String name;
    
    private String value;
}

In this case, we need to deal with the construction process of deserialized related classes

@Data
public class Documentation {

    private String name;
    
    private String value;

    /**
     * The required for deserialization supports the scenario of Documentation: ''.
     * @param str
     * @return
     * @throws IOException
     */
    @JsonCreator
    public static Documentation create(String str) throws IOException {
        //This place is for processing ''
        if (StringUtils.isBlank(str)) {
            return null;
        }
        //In fact, value is extracted separately and deserialized again;
        return (new ObjectMapper()).readValue(str, Documentation.class);
    }
}

explain:

  1. During deserialization, Jackson will call the object's parameterless constructor by default. If we do not define any constructor, the JVM will be responsible for generating the default parameterless constructor. However, if we define a constructor and do not provide a parameterless constructor, Jackson will report an error;
  2. @JsonCreator this annotation is used to specify a specific constructor or factory method when deserializing an object. If the default constructor cannot meet the requirements, or we need to do some special logic when constructing objects, we can use this annotation.
  3. If it is a constructor, it needs to be used in conjunction with @ JsonProperty:
public class Person {
 
    private int age;
    private String name;
 
    @JsonCreator
    public Person(@JsonProperty("age") int age, @JsonProperty("name") String name) {
        this.age = age;
        this.name = name;
    }
}
  1. If it is a static method, it is not required; The example is the Documentation example I used above.

summary

Jack JSON is really much better than fast JSON in these special processing.

Assuming that the default deserialization constructor (no parameters) cannot meet our requirements, we can specify the constructor or static method through @ JsonCreator. The constructor needs to be used with @ JsonProperty, while the static method does not.

Attachment configuration details

 //This feature determines whether the parser will automatically close the input sources that do not belong to the parser itself.
// If prohibited, the calling application has to close the basic input streams InputStream and reader used to create the parser respectively;
//The default is true
objectMapper.configure(JsonParser.Feature.AUTO_CLOSE_SOURCE, true);
//Whether to allow parsing of comments using Java/C + + style (including '/' + '*' and '/ /' variables)
objectMapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);

//When set to true, the property name is not enclosed in double quotes
objectMapper.configure(JsonGenerator.Feature.QUOTE_FIELD_NAMES, false);
//Deserialization is whether attribute names are allowed without double quotes
objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);

//Whether single quotation marks are allowed to enclose attribute names and string values
objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);

//Whether JSON strings are allowed to contain non quote control characters (ASCII characters with values less than 32, including tabs and line breaks)
objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);

//Allow JSON integers to start with multiple zeros
objectMapper.configure(JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS, true);

//null properties are not serialized
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

//Sort attributes alphabetically. The default is false
objectMapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY,true);

//Whether to take the class name as the root element, you can customize the root element name through @ JsonRootName. The default is false
objectMapper.configure(SerializationFeature.WRAP_ROOT_VALUE,true);

//Whether to scale and arrange the output. The default is false
objectMapper.configure(SerializationFeature.INDENT_OUTPUT,false);

//When serializing Date, it is output as timestamps. The default is true
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,true);

//Whether the serialized enumeration is output as toString(), which is false by default, that is, it is output as name() by default
objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true);

//Whether the serialized enumeration is output as ordinal(), which is false by default
objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_INDEX,false);

//When serializing a single element array, it is not output as an array. The default is false
objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING,true);

//When serializing a Map, sort the key s. The default is false
objectMapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS,true);

//When serializing char [], it is output as json array. The default is false
objectMapper.configure(SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS,true);

//Whether to output the original number or scientific count when serializing BigDecimal. The default is false, that is, it is output in the form of toPlainString() scientific count
objectMapper.configure(SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN,true);
@JsonFormat(pattern = "yyyy-MM-dd'T' HH:mm:ss:SSS'Z'",timezone = "GMT+8")
Time format annotation type must be Date,Otherwise, it will not take effect

Direct reference address

Deserializing stringified (quote enclosed) nested objects with Jackson

Jackson framework tutorial

jackson json conversion entity allows special characters and escape characters, single quotation marks

Indirect reference

https://blog.csdn.net/java821643/article/details/103179040

Keywords: Java

Added by xepherys on Sat, 15 Jan 2022 10:02:13 +0200