Jackson的进阶<二>

readValue

POJO对象

User:

public class User {

    private String name;
    private int age;

    //省略get、set、toString方法   
 }
        ObjectMapper mapper = new ObjectMapper();
        String json = "{'name':'gakkij','age':18}";
        User user = mapper.readValue(json, User.class);
        System.out.println(user);

运行:

Exception in thread "main" com.fasterxml.jackson.core.JsonParseException: Unexpected character (''' (code 39)): was expecting double-quote to start field name
 at [Source: (String)"{'name':'gakkij','age':18}"; line: 1, column: 3]

报错:因为好像Jackson的json字符串必须要符合标准的形式,字符串中的key、vlaue如果是String类型,必须是双引号!!!

之前,我们使用Gson或FastJson时,因为不想写转义符:\,因此都是使用单引号来表示字符串的。

Jackson必须使用双引号:

        ObjectMapper mapper = new ObjectMapper();
        String json = "{\"name\":\"gakkij\",\"age\":18}";
        User user = mapper.readValue(json, User.class);
        System.out.println(user);

输出:

User{name='gakkij', age=18}

当反序列化的字符串中有POJO中不存在的属性时:

        ObjectMapper mapper = new ObjectMapper();
        String json = "{\"name\":\"gakkij\",\"age\":18,\"address\":\"东京\"}";
        User user = mapper.readValue(json, User.class);
        System.out.println(user);

这里,添加了address的key,但是User中是没有address的属性的。

运行后:

Exception in thread "main" com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "address" (class com.liuzhuo.jackson.domain.User), not marked as ignorable (2 known properties: "name", "age"])
 at [Source: (String)"{"name":"gakkij","age":18,"address":"东京"}"; line: 1, column: 38] (through reference chain: com.liuzhuo.jackson.domain.User["address"])

出现,不能解析address属性的异常!!!

这里,我们需要配置mapper的特性,来控制当出现无法解析的属性时,不需要抛出异常!

  • mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

  • mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

        ObjectMapper mapper = new ObjectMapper();
        //mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);//和上面等价
        String json = "{\"name\":\"gakkij\",\"age\":18,\"address\":\"东京\"}";
        User user = mapper.readValue(json, User.class);
        System.out.println(user);

List类型

        ObjectMapper mapper = new ObjectMapper();
        mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        String json = "[{\"name\":\"gakkij\",\"age\":18,\"address\":\"东京\"},{\"name\":\"jackLove\",\"age\":20,\"address\":\"冲绳\"}]";
        List list = mapper.readValue(json, List.class);
        User[] users = mapper.readValue(json, User[].class);
        System.out.println(list);
        System.out.println(users);

输出:

[{name=gakkij, age=18, address=东京}, {name=jackLove, age=20, address=冲绳}]
[Lcom.liuzhuo.jackson.domain.User;@91161c7

这里看来没有问题,当我们使用带有泛型的List集合呢?

        ObjectMapper mapper = new ObjectMapper();
        mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        String json = "[{\"name\":\"gakkij\",\"age\":18,\"address\":\"东京\"},{\"name\":\"jackLove\",\"age\":20,\"address\":\"冲绳\"}]";
        List<User> userList = mapper.readValue(json, List.class);
        System.out.println(userList);

输出:

[{name=gakkij, age=18, address=东京}, {name=jackLove, age=20, address=冲绳}]

看似没有出错,但是你们注意到了吗?此时的User对象居然打印出了address!!!颠覆了我们的认知,我们的User对象里面是没有address属性的。

        List<User> userList = mapper.readValue(json, List.class);
        userList.forEach(user -> {
            System.out.println(user);
        });

当我们遍历List集合来打印时,问题就出来了:

Exception in thread "main" java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.liuzhuo.jackson.domain.User
    at java.util.ArrayList.forEach(ArrayList.java:1257)
    at com.liuzhuo.jackson.jacksonDataBindTest02.testDataBindListPOJO2(jacksonDataBindTest02.java:48)
    at com.liuzhuo.jackson.jacksonDataBindTest02.main(jacksonDataBindTest02.java:59)

说明,此时List<user>根本不是我们想象中的List<User>

带泛型的集合,必须使用:

  • objectMapper.getTypeFactory().constructParametricType(collectionClass, elementClasses);
  • TypeReference<>的子类

1)使用:constructParametricType

        ObjectMapper mapper = new ObjectMapper();
        mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        String json = "[{\"name\":\"gakkij\",\"age\":18,\"address\":\"东京\"},{\"name\":\"jackLove\",\"age\":20,\"address\":\"冲绳\"}]";
        JavaType javaType = mapper.getTypeFactory().constructParametricType(List.class, User.class);
        List<User> userList = mapper.readValue(json, javaType);
        userList.forEach(user -> {
            System.out.println(user);
        });
        System.out.println(userList);

2)使用:TypeReference<>的子类

        ObjectMapper mapper = new ObjectMapper();
        mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        String json = "[{\"name\":\"gakkij\",\"age\":18,\"address\":\"东京\"},{\"name\":\"jackLove\",\"age\":20,\"address\":\"冲绳\"}]";
        TypeReference<List<User>> type = new TypeReference<List<User>>() {
        };
        List<User> userList = mapper.readValue(json, type);
        userList.forEach(user -> {
            System.out.println(user);
        });
        System.out.println(userList);

输出:

User{name='gakkij', age=18}
User{name='jackLove', age=20}
[User{name='gakkij', age=18}, User{name='jackLove', age=20}]

Map类型

1)不带泛型的Map解析

        ObjectMapper mapper = new ObjectMapper();
        mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        String json = "{\"a\":\"1\",\"b\":2,\"c\":true}";
        Map map = mapper.readValue(json, Map.class);
        System.out.println(map);
{a=1, b=2, c=true}
        ObjectMapper mapper = new ObjectMapper();
        mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        String json = "{\"a\":\"1\",\"b\":2,\"c\":true}";
        Map map = mapper.readValue(json, Map.class);
        Object a = map.get("a");
        System.out.println(a.getClass());
        System.out.println(a);
        Object b = map.get("b");
        System.out.println(b.getClass());
        System.out.println(b);
        Object c = map.get("c");
        System.out.println(c.getClass());
        System.out.println(c);
        System.out.println(map);
class java.lang.String
1
class java.lang.Integer
2
class java.lang.Boolean
true
{a=1, b=2, c=true}

2)带泛型的Map解析

  • mapper.getTypeFactory().constructParametricType(MapClass, keyClass, valueClass)

  • new TypeReference<>() {}

方法一:

        ObjectMapper mapper = new ObjectMapper();
        mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

        String json = "{\"a\":\"1\",\"b\":2,\"c\":true}";
        JavaType javaType = mapper.getTypeFactory().constructParametricType(HashMap.class, String.class, String.class);
        HashMap<String, String> map = mapper.readValue(json, javaType);
        Object a = map.get("a");
        System.out.println(a.getClass());
        System.out.println(a);
        Object b = map.get("b");
        System.out.println(b.getClass());
        System.out.println(b);
        Object c = map.get("c");
        System.out.println(c.getClass());
        System.out.println(c);
        System.out.println(map);

输出:

class java.lang.String
1
class java.lang.String
2
class java.lang.String
true
{a=1, b=2, c=true}

此时,都是Map中的key和value都是字符串类型。


方法二:

        TypeReference<HashMap<String, String>> type = new TypeReference<HashMap<String, String>>() {
        };
        HashMap<String, String> map = mapper.readValue(json, type);

总结

Jackson处理一般的JavaBean和Json之间的转换只要使用ObjectMapper 对象的readValuewriteValueAsString两个方法就能实现。

但是如果要转换复杂类型Collection如List<YourBean>,那么就需要先反序列化复杂类型为泛型的Collection Type。

如果是ArrayList<YourBean>那么使用ObjectMapper 的getTypeFactory().constructParametricType(collectionClass, yourBean.class);

如果是HashMap<String,YourBean>那么 ObjectMapper 的getTypeFactory().constructParametricType(MapClass,String.class, YourBean.class);

带泛型的 List 和 Map ,也可以统一使用:new TypeReference<>() {}的子类来实现。

例如:new TypeReference<List<youBean>(){}new TypeReference<HashMap<String,youBean>(){}


writeValue

默认序列化:

        ObjectMapper mapper = new ObjectMapper();
        User user = new User("gakki", 18);
        byte[] bytes = mapper.writeValueAsBytes(user);
        String userStr = mapper.writeValueAsString(user);
        System.out.println(bytes);
        System.out.println(userStr);

输出:

[B@2669b199
{"name":"gakki","age":18}

当POJO对象中存在null值时:

        ObjectMapper mapper = new ObjectMapper();
        User user = new User();
        user.setName("jacklove");
        String userStr = mapper.writeValueAsString(user);
        System.out.println(userStr);
{"name":"jacklove","age":0}

这里因为,age是基本类型int,有默认值:0。


User:

public class User {
    private String name;
    private int age;
    private String address;  //多加一个引用属性
    ···
}
{"name":"jacklove","age":0,"address":null}

Jackson默认是会帮我们序列化null的属性值的,Gson和FastJson默认是不会帮我们序列化null值!

不序列化null

不想序列化null值呢?使用如下配置:

mapper.setDefaultPropertyInclusion(JsonInclude.Include.NON_NULL);
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        ObjectMapper mapper = new ObjectMapper();
        //设置属性特性,不包括null的值
        mapper.setDefaultPropertyInclusion(JsonInclude.Include.NON_NULL);
        //mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        User user = new User();
        user.setName("jacklove");
        String userStr = mapper.writeValueAsString(user);
        System.out.println(userStr);

输出:

{"name":"jacklove","age":0}

Incluede的参数配置:

Include.Include.ALWAYS 默认   
Include.NON_DEFAULT 属性为默认值不序列化   
Include.NON_EMPTY 属性为 空("") 或者为 null 都不序列化   
Include.NON_NULL 属性为NULL 不序列化  

日期格式化:

User

public class User {

    private String name;
    private int age;
    private String address;
    private Date birthday;  //添加一个时间类型
    ···  
 }
        ObjectMapper mapper = new ObjectMapper();
        mapper.setSerializationInclusion(JsonInclude.Include.NON_DEFAULT);
        User user = new User();
        user.setName("jacklove");
        user.setBirthday(new Date()); //日期默认序列化为时间戳,不够友好。
        String userStr = mapper.writeValueAsString(user);
        System.out.println(userStr);
{"name":"jacklove","birthday":1577606398838}

        ObjectMapper mapper = new ObjectMapper();
        mapper.setSerializationInclusion(JsonInclude.Include.NON_DEFAULT);
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        //设置日期的格式
        mapper.setDateFormat(dateFormat);
        User user = new User();
        user.setName("jacklove");
        user.setBirthday(new Date()); //日期默认序列化为时间戳,不够友好。
        String userStr = mapper.writeValueAsString(user);
        System.out.println(userStr);

输出:

{"name":"jacklove","birthday":"2019-12-29"}

格式化序列化

将序列化后的字符串以人为友好的形式:

        ObjectMapper mapper = new ObjectMapper();
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        mapper.setDateFormat(dateFormat);
        //友好的形式
        mapper.enable(SerializationFeature.INDENT_OUTPUT);

        User user = new User();
        user.setName("jacklove");
        user.setBirthday(new Date()); //日期默认序列化为时间戳,不够友好。
        String userStr = mapper.writeValueAsString(user);
        System.out.println(userStr);

输出:

{
  "name" : "jacklove",
  "age" : 0,
  "address" : null,
  "birthday" : "2019-12-29"
}

readTree

1)使用:mapper.readTree(); //构建JsonNode

        ObjectMapper mapper = new ObjectMapper();
        User user = new User("gakki", 18);
        String json = mapper.writeValueAsString(user);
        System.out.println(json);

        //根节点
        JsonNode jsonNode = mapper.readTree(json);
        System.out.println(jsonNode.getNodeType());
        System.out.println(jsonNode);
        boolean name = jsonNode.has("name");
        System.out.println(name);
        JsonNode name1 = jsonNode.get("name");
        System.out.println(name1);
        System.out.println(jsonNode.get("age"));
        System.out.println(jsonNode.has("address"));//虽然addrss为null,但是返回true
        System.out.println(jsonNode.get("address"));//address存在,但是值为null
        System.out.println(jsonNode.has("notExit"));//不存在,返回false
        System.out.println(jsonNode.get("notExit"));//返回null,是本身不存在,返回null

输出:

{"name":"gakki","age":18,"address":null,"birthday":null}
OBJECT
{"name":"gakki","age":18,"address":null,"birthday":null}
true
"gakki"
18
true
null
false
null

2)使用JsonNodeFactory来创建ObjectNode和ArrayNode节点

        ObjectMapper mapper = new ObjectMapper();
        JsonNodeFactory nodeFactory = mapper.getNodeFactory();

        ObjectNode objNode = nodeFactory.objectNode();
        objNode.put("name", "gakki");
        objNode.put("age", 18);
        ObjectNode boyfriend = objNode.putObject("boyfriend"); // "boyfriend":{
        boyfriend.put("name", "liuzhuo");
        boyfriend.put("age", 20);
        ArrayNode hobby = objNode.putArray("hobby"); // "hobby":[
        hobby.add("看电影");
        hobby.add("爬山");
        hobby.add("游泳");
        hobby.add("读书");
        String value = mapper.writeValueAsString(objNode);
        System.out.println(value);
{"name":"gakki","age":18,"boyfriend":{"name":"liuzhuo","age":20},"hobby":["看电影","爬山","游泳","读书"]}

总结

  • 平时一般使用readValue,将json字符串反序列化为POJO对象,注意,json字符串有多余的属性默认会报错,必须配置:mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)。POJO的属性如果要反序列化成功,要么使用public修饰,要么配上相应的get和set方法。
  • readValue带泛型的POJO:方法一:TypeReference<>的子类,方法二:mapper.getTypeFactory().constructParametricType(collectionClass, yourBean.class);
  • writerValue: 序列化POJO对象,默认会序列化值为null、空字符串等,想去掉使用:mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
  • readTree:是比较底层的用法了,更加灵活一些,可以自己组建要序列化的JsonNode。

  转载请注明: 解忧杂货店 Jackson的进阶<二>

  目录