Jackson用法并解决反序列化嵌套带引号的字符串对象

环境

java:1.8+
jackson-databind:2.9.1

前言

最近做需求时,解析的json比较奇特,里面嵌套多层json,并且这些嵌套的json里面会存在单引号,利用Java存进入数据库后,又会存在转义符,这样反序列化时,就会报错,解析失败。
刚开始,我使用的是fastJson来反序列化,但是在单引号、转义符方面,支持的真的不够好。
后面改为使用jackson,它对转义符、单引号、null、空串等,有非常好的解决方案。

引入依赖

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

使用方法

创建objectMapper

第一步,我们需要创建一个ObjectMapper对象,通过它我们可以序列化或者反序列化

ObjectMapper objectMapper = new ObjectMapper();

序列化和反序列化的简单使用

如果是简单的解析:

User user = new User();
user.setName("rongK");
user.setAge(28);
user.setCreateTime(new Timestamp(new Date().getTime()));
//序列化
String s = objectMapper.writeValueAsString(user);
System.out.println("对象序列化:" + s);// 对象序列化:{"name":"rongK","age":28,"createTime":1598081001761}
//反序列化
User u = objectMapper.readValue(s, User.class);
System.out.println("对象反序列化:" + u);// 对象反序列化:User(name=rongK, age=28, createTime=2020-08-22 15:23:21.761)

上面的例子只是简单的使用,但是会存在这下列的一些情况:

问题① 假设反序列化的json里面有个字段,在POJO类中没有,会反序列失败

问题② 反序列化的json格式存在一定问题:

如果是以下情况,也会反序列化失败:

{
	//User是个POJO,并不是个字符串
	//默认情况下,反序列化会失败
	"User": ""
}

问题③反序列化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)}"

可以看出value是个字符串,但是该字符串里面存在单引号双引号的嵌套使用。

问题④ 如果存在转义符或特殊字符,反序列化也会失败

针对使用问题,Jackjson可以通过一些配置来帮我们解决掉;

解决办法

忽略没有映射的字段

我们反序列化的时候,在类中往往会有json中不存在的字段,比如:
有个Properties类,里面只定义了两个字段:

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

但是我们的json字符串里面却有Properties类没有定义的字段:

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

这个时候我们就需要忽略映射的字段

方法一:

@JsonIgnoreProperties(ignoreUnknown = true)

在POJO类中,
缺点,POJO里面组合的POJO类也得加

方法二:

建议使用

objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

单引号的解决办法

//允许出现单引号
objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true) ;

null和空串的解决办法

//支持null和空串
        objectMapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);

这里需要说明下,以下场景,即使进行了上面的设置,依然不能解决问题

{
	//User是个POJO,并不是个字符串
	//默认情况下,反序列化会失败
	"User": ""
}

解决转义符和特殊字段的方法

//允许出现特殊字符和转义符
objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true) ;

解决用Jackson反序列化嵌套的字符串对象(带引号)

假设有如下

{
    "id": "abcd1234",
    "name": "test",
    "connections": {
        "default": "http://foo.com/api/",
        "dev": "http://dev.foo.com/api/v2"
    },
    "document": "",//document本质是个类,里面有两个字段
    "settings": {
        "foo": "{\n \"fooId\": 1, \"token\": \"abc\"}",
        "bar": "{\"barId\": 2, \"accountId\": \"d7cj3\"}"
    }
}
@Data
public class Documentation {

    private String name;
    
    private String value;
}

这种情况下,我们需要对反序列化的相关类的的构建过程进行处理

@Data
public class Documentation {

    private String name;
    
    private String value;

    /**
     * 反序列化时需要用的,支持Documentation:""的场景。
     * @param str
     * @return
     * @throws IOException
     */
    @JsonCreator
    public static Documentation create(String str) throws IOException {
        //这个地方是对""进行处理
        if (StringUtils.isBlank(str)) {
            return null;
        }
        //这里其实就是将value单独抽出来,再次进行反序列化;
        return (new ObjectMapper()).readValue(str, Documentation.class);
    }
}

说明:

  1. 在反序列化时,Jackson默认会调用对象的无参构造函数,如果我们不定义任何构造函数,JVM会负责生成默认的无参构造函数。但是如果我们定义了构造函数,并且没有提供无参构造函数时,Jackson会报错;
  2. @JsonCreator 该注解用在对象的反序列时指定特定的构造函数或者工厂方法。如果默认构造函数无法满足需求,或者说我们需要在构造对象时做一些特殊逻辑,可以使用该注解。
  3. 如果是构造函数,则需要配合@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. 如果是静态方法,则不需要;例子就是我上面用的Documentation例子。

支持结尾逗号的场景

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=2021年09月30日-=-=-=-=-=-=-=-=-start=-=-=-=-=-=
今天遇到了json字符串中末尾存在逗号的情况:

{"solutionGroup":"insuranceDealSolution",
"processType": "TRANSFER",}

这种情况也会反序列化失败;

解决办法:

//支持结尾逗号
objectMapper.configure(JsonParser.Feature.ALLOW_TRAILING_COMMA, true);

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=2021年09月30日-=-=-=-=-=-=-=-=-end=-=-=-=-=-=

总结

Jackjson在这些特殊处理方面,确实要比fastjson要好用的多。

假设默认的反序列化的构造方法(无参数)不能满足我们的要求,那么我们就可以通过@JsonCreator指定构造函数或者静态方法,其中构造方法需要配合@JsonProperty来使用,而静态方法则不需要。

附件配置详细说明

 //这个特性,决定了解析器是否将自动关闭那些不属于parser自己的输入源。
// 如果禁止,则调用应用不得不分别去关闭那些被用来创建parser的基础输入流InputStream和reader;
//默认是true
objectMapper.configure(JsonParser.Feature.AUTO_CLOSE_SOURCE, true);
//是否允许解析使用Java/C++ 样式的注释(包括'/'+'*' 和'//' 变量)
objectMapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);

//设置为true时,属性名称不带双引号
objectMapper.configure(JsonGenerator.Feature.QUOTE_FIELD_NAMES, false);
//反序列化是是否允许属性名称不带双引号
objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);

//是否允许单引号来包住属性名称和字符串值
objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);

//是否允许JSON字符串包含非引号控制字符(值小于32的ASCII字符,包含制表符和换行符)
objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);

//是否允许JSON整数以多个0开始
objectMapper.configure(JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS, true);

//null的属性不序列化
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

//按字母顺序排序属性,默认false
objectMapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY,true);

//是否以类名作为根元素,可以通过@JsonRootName来自定义根元素名称,默认false
objectMapper.configure(SerializationFeature.WRAP_ROOT_VALUE,true);

//是否缩放排列输出,默认false
objectMapper.configure(SerializationFeature.INDENT_OUTPUT,false);

//序列化Date日期时以timestamps输出,默认true
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,true);

//序列化枚举是否以toString()来输出,默认false,即默认以name()来输出
objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true);

//序列化枚举是否以ordinal()来输出,默认false
objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_INDEX,false);

//序列化单元素数组时不以数组来输出,默认false
objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING,true);

//序列化Map时对key进行排序操作,默认false
objectMapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS,true);

//序列化char[]时以json数组输出,默认false
objectMapper.configure(SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS,true);

//序列化BigDecimal时是输出原始数字还是科学计数,默认false,即以toPlainString()科学计数方式来输出
objectMapper.configure(SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN,true);
@JsonFormat(pattern = "yyyy-MM-dd'T' HH:mm:ss:SSS'Z'",timezone = "GMT+8")
时间格式注解 类型必须是Date,否则不生效

直接参考地址

Deserializing stringified (quote enclosed) nested objects with Jackson

Jackson框架使用教程

jackson json转实体 允许特殊字符和转义字符 单引号

Add JsonParser.ALLOW_TRAILING_COMMA to work for Arrays and Objects

间接参考

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

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

山鬼谣me

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值