Jackson系列-ObjectMapper的使用

2,105 阅读2分钟

前言

在jackson中,最常见的就是通过ObjectMapper进行序列化和反序列化。ObjectMapper是一个通用的对象,它不仅可以对JSON格式进行操作,也可以对其它数据格式进行操作。而JSonMapper是JSON格式的专用操作对象

序列化与反序列化

把ObjectMapper的序列化和反序列化操作封装成一个工具类,供全局使用,因为ObjectMapper里面的序列化和反序列化逻辑都做了同步处理,因此不用担心出现线程安全的问题。

public class JsonUtil {
    private static final ObjectMapper objectMapper = new ObjectMapper();

    static {
        objectMapper.enable(MapperFeature.PROPAGATE_TRANSIENT_MARKER);
    }

    // 序列化
    public static <T> String obj2String(T obj) {
        if (obj == null) {
            return null;
        }
        try {
            return obj instanceof String ? (String) obj : objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    // 反序列化
    public static <T> T string2Obj(String str, Class<T> clazz) {
        if (StringUtils.isEmpty(str) || clazz == null) {
            return null;
        }
        try {
            return clazz.equals(String.class) ? (T) str : objectMapper.readValue(str, clazz);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}
===
Studentpublic class Student {
    @JsonProperty(value = "studentName")
    private String name;
    @JsonProperty(value = "studentAge")
    private Integer age;

    private String profileImageUrl;

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date birthdate;

    public String getName() {
        return name;
    }

    public Student setName(String name) {
        this.name = name;
        return this;
    }

    public Integer getAge() {
        return age;
    }

    public Student setAge(Integer age) {
        this.age = age;
        return this;
    }

    public String getProfileImageUrl() {
        return profileImageUrl;
    }

    public Student setProfileImageUrl(String profileImageUrl) {
        this.profileImageUrl = profileImageUrl;
        return this;
    }

    public Date getBirthdate() {
        return birthdate;
    }

    public Student setBirthdate(Date birthdate) {
        this.birthdate = birthdate;
        return this;
    }


    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", profileImageUrl='" + profileImageUrl + '\'' +
                ", birthdate=" + birthdate +
                '}';
    }
}
===
@Test
    public void obj2String() {
        Student student = new Student();
        student.setAge(20);
        student.setName("json");
        student.setProfileImageUrl("www.baidu.com");
        student.setBirthdate(new Date());
        System.out.println(JsonUtil.obj2String(student));
    }

    @Test
    void string2Obj() {
        String json = "{\n" +
                "  \"profileImageUrl\" : \"www.baidu.com\",\n" +
                "  \"birthdate\" : \"2020-08-07 00:13:07\",\n" +
                "  \"studentName\" : \"jhon\",\n" +
                "  \"studentAge\" : 20,\n" +
                "  \"father\": {\n" +
                "    \"name\": \"mac\"\n" +
                "   }\n" +
                "}";
        Student student = JsonUtil.string2Obj(json, Student.class);
        System.out.println(student);
    }

泛型擦除问题

    @Test
    public void testRead() throws IOException {
        ObjectMapper objectMapper = new ObjectMapper();
        String idsStr = "[1,2,3]";
        List<Long> list = objectMapper.readValue(idsStr, List.class);
        Long i1 = list.get(0);
        System.out.println(list);
    }

上面的例子会抛出如下错误:

java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Long

所以解决办法有两个

  1. 使用成员变量保存泛型
    public class data {
        private List<Long> numbers;

        public List<Long> getNumbers() {
            return numbers;
        }

        public data setNumbers(List<Long> numbers) {
            this.numbers = numbers;
            return this;
        }
    }
    
        @Test
    public void readGeneric() throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        String numbers = "{\"numbers\" : [1,2,3]}";
        Data d = objectMapper.readValue(numbers, Data.class);
        Long i1 = d.getNumbers().get(0);
        System.out.println(i1);
    }

但是这种方式不直接,还得把json数据包一层 2. 使用TypeReference

    @Test
  public void readGeneric() throws JsonProcessingException {
      ObjectMapper objectMapper = new ObjectMapper();
      String numbers = "{\"numbers\" : [1,2,3]}";
      List<Long> longList = objectMapper.readValue(numbers, new TypeReference<List<Long>>() {
      });
      Long i1 = longList.get(0);
      System.out.println(i1);
  }

TypeReference能够将泛型的类型保存下来,比如如果想反序列化成Map<String,String>,那么提供下面

objectMapper.readValue(jsonStr,new TpeReference<Map<String,String>>(){}

树模型

在一个json字符串中,如果只想取某一个属性的值,树模型可以非常简单的实现

{"name":"generator-test","age":22,"object":{"name":"nested-object","value":"this is a test"},"list":["computer","helicopter"]}

如果只想获得list

    @Test
  void treeNode() throws JsonProcessingException {
      String jsonStr = "{\"name\":\"generator-test\",\"age\":22,\"object\":{\"name\":\"nested-object\",\"value\":\"this is a test\"},\"list\":[\"computer\",\"helicopter\"]}";
      ObjectMapper objectMapper = new ObjectMapper();
      JsonNode jsonNode = objectMapper.readTree(jsonStr).get("list");
      int i = 0;
      while (jsonNode.hasNonNull(i)) {
          System.out.println(jsonNode.get(i).asText());
          i++;
      }
  }

这样就不用专门构造POJO来接受list啦