• Jackson序列化与反序列化


    一、Jackson简介

    1、什么是Jackson

    Jackson被认为是"Java JSON库"或"Java最好的JSON解析器"。或简单地被当作"JSON for Java"。不仅如此,Jackson 还是一套用于 Java(和 JVM 平台)的数据处理工具,包括流式 JSON parser / generator库、匹配 data-binding 库(POJO和JSON相互转换),还有一个额外的 data format 模块来处理 Avro, BSON, CBOR, CSV, Smile, (Java) Properties, Protobuf, TOML, XML, YAML 这些数据编码,甚至还有大量的数据格式模块,来支持被广泛使用的数据类型如 Guava, Joda, PCollections 等等

    核心组件存在于他们自己的项目下,包括三个核心包(streaming, databind, annotations);数据格式库;数据类型库;JAX-RS provider;和一个复杂的扩展模块—这个project 连接各个模块的中心枢纽

    2、核心模块

    核心模块是扩展(模块)构建的基础。目前有3个模块 (Jackson 2.x为例) :

    • Streaming (docs) (“jackson-core”) 定义低级流 API,并包括 JSON 具体实现

    • Annotations (docs) (“jackson-annotations”) 包含标准 Jackson 注解

    • Databind (docs) (“jackson-databind”) 实现data-binding (和 object serialization) ,支持 streaming 包; 它依赖于 streamingannotations

    二、ObjectMapper常见使用

    ObjectMapper类(com.fasterxml.jackson.databind.ObjectMapper)是Jackson的主要类,它可以帮助我们快速的进行各个类型和Json类型的相互转换

    1、ObjectMapper的常用配置

    private static final ObjectMapper mapper;
    public static ObjectMapper getObjectMapper(){
        return this.mapper;
    }
    static{
        //创建ObjectMapper对象
        mapper = new ObjectMapper()
        //configure方法 配置一些需要的参数
        // 转换为格式化的json 显示出来的格式美化
        mapper.enable(SerializationFeature.INDENT_OUTPUT);
        //序列化的时候序列对象的那些属性  
        //JsonInclude.Include.NON_DEFAULT 属性为默认值不序列化 
        //JsonInclude.Include.ALWAYS      所有属性
        //JsonInclude.Include.NON_EMPTY   属性为 空(“”) 或者为 NULL 都不序列化 
        //JsonInclude.Include.NON_NULL    属性为NULL 不序列化
        mapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);  
        //反序列化时,遇到未知属性会不会报错 
        //true - 遇到没有的属性就报错 false - 没有的属性不会管,不会报错
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        //如果是空对象的时候,不抛异常  
        mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);  
        // 忽略 transient 修饰的属性
        mapper.configure(MapperFeature.PROPAGATE_TRANSIENT_MARKER, true);
        //修改序列化后日期格式
        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);  
        mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        //处理不同的时区偏移格式
        mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        mapper.registerModule(new JavaTimeModule());
    }
    

    2、ObjectMapper的常用方法

    2.1 json字符串转对象

    ObjectMapper mapper = new ObjectMapper();
    String jsonString = "{\"name\":\"shawn\", \"age\":20}";
     
    //将字符串转换为对象
    Student student = mapper.readValue(jsonString, Student.class);
    System.out.println(student);
     
    //将对象转换为json字符串
    jsonString = mapper.writeValueAsString(student);
    System.out.println(jsonString);
     
     
    结果:
    Student [ name: shawn, age: 20 ]
     
    {
      "name" : "Hyl",
      "age" : 20
    }
    

    2.2 数组和对象之间转换

    //对象转为byte数组
    byte[] byteArr = mapper.writeValueAsBytes(student);
    System.out.println(byteArr);
     
    //byte数组转为对象
    Student student= mapper.readValue(byteArr, Student.class);
    System.out.println(student);
     
    结果:
    [B@3327bd23
    Student [ name: shawn, age: 20 ]
    

    2.3 集合和json字符串之间转换

    List<Student> studentList= new ArrayList<>();
    studentList.add(new Student("shawn1" ,20 , new Date()));
    studentList.add(new Student("shawn2" ,21 , new Date()));
    studentList.add(new Student("shawn3" ,22 , new Date()));
    studentList.add(new Student("shawn4" ,23 , new Date()));
     
    String jsonStr = mapper.writeValueAsString(studentList);
    System.out.println(jsonStr);
            
    List<Student> studentList2 = mapper.readValue(jsonStr, List.class);
    System.out.println("字符串转集合:" + studentList2 );
     
    结果:
    [ {
      "name" : "shawn1",
      "age" : 20,
      "sendTime" : 1525164212803
    }, {
      "name" : "shawn2",
      "age" : 21,
      "sendTime" : 1525164212803
    }, {
      "name" : "shawn3",
      "age" : 22,
      "sendTime" : 1525164212803
    }, {
      "name" : "shawn4",
      "age" : 23,
      "sendTime" : 1525164212803
    } ]
    [{name=shawn1, age=20, sendTime=1525164212803}, {name=shawn2, age=21, sendTime=1525164212803}, {name=shawn3, age=22, sendTime=1525164212803}, {name=shawn4, age=23, sendTime=1525164212803}]
    

    2.4 map和json字符串之间转换

    Map<String, Object> testMap = new HashMap<>();
    testMap.put("name", "22");
    testMap.put("age", 20);
    testMap.put("date", new Date());
    testMap.put("student", new Student("shawn", 20, new Date()));
     
     
    String jsonStr = mapper.writeValueAsString(testMap);
    System.out.println(jsonStr);
    Map<String, Object> testMapDes = mapper.readValue(jsonStr, Map.class);
    System.out.println(testMapDes);
     
    结果:
    {
      "date" : 1525164212803,
      "name" : "22",
      "student" : {
        "name" : "shawn",
        "age" : 20,
        "sendTime" : 1525164212803,
        "intList" : null
      },
      "age" : 20
    }
    {date=1525164212803, name=22, student={name=shawn, age=20, sendTime=1525164212803, intList=null}, age=20}
    

    2.5 日期转json字符串

    // 修改时间格式
    mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
    Student student = new Student ("shawn",21, new Date());
    student.setIntList(Arrays.asList(1, 2, 3));
    
    String jsonStr = mapper.writeValueAsString(student);
    System.out.println(jsonStr);
     
    结果:
    {
      "name" : "shawn",
      "age" : 21,
      "sendTime" : "2020-07-23 13:14:36",
      "intList" : [ 1, 2, 3 ]
    }
    

    2.6 readTree()方法

    此方法更灵活,可以只将用户感兴趣的Json串信息值提取出来。主要利用ObjectMapper提供的readTree和Jackson提供的JsonNode类来实现

    String test="{"results":[{"objectID":357,"geoPoints":[{"x":504604.59802246094,"y":305569.9150390625}]},{"objectID":358,"geoPoints":[{"x":504602.2680053711,"y":305554.43603515625}]}]}";
    //此Json串比较复杂,包含了嵌套数组的形式,具有通用性。
    //2.2.2.2实现反序列化
    JsonNode node= objectMapper.readTree(test); //将Json串以树状结构读入内存
    JsonNode contents=node.get("results");//得到results这个节点下的信息
    //遍历results下的信息,size()函数可以得节点所包含的的信息的个数,类似于数组的长度
    for(int i=0;i<contents.size();i++)  {
        //读取节点下的某个子节点的值
        System.out.println(contents.get(i).get("objectID").getIntValue());
        JsonNode geoNumber=contents.get(i).get("geoPoints");
        //循环遍历子节点下的信息
        for(int j=0;j<geoNumber.size();j++){
            System.out.println(geoNumber.get(j).get("x").getDoubleValue()+"  "+geoNumber.get(j).get("y").getDoubleValue());
        }
    }
    
    

    3、Java Web与ObjectMapper

    在开发 Spring Web 应用程序时,如果自定义了 ObjectMapper,并把它注册成了Bean,那很可能会导致 Spring Web 使用的 ObjectMapper 也被替换,导致 Bug。例如下面的bean,注册到Spring后就会把Spring原有的配置覆盖,导致原有的序列化配置丢失

    @Bean
    public ObjectMapper objectMapper(){
        ObjectMapper objectMapper=new ObjectMapper();
        objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_INDEX,true);
        return objectMapper;
    }
    
    

    针对这个问题,有三种解决方法

    • 使用objectMapper.configure(SerializationFeature.xxx,true);把配置补齐

    • 设置自定义类型,加上 @JsonIgnoreProperties 注解,开启 ignoreUnknown
      属性,以实现反序列化时忽略额外的数据

    • 不要自定义 ObjectMapper,而是直接在配置文件设置相关参数,来修改
      Spring 默认的 ObjectMapper 的功能,例如:spring.jackson.serialization.write_enums_using_index=true

    另外,通过查找JacksonProperties类源码,可以发现很多配置类的属性,可以配合使用

    4、Redis序列化的一个例子

    @Bean
    public <T> RedisTemplate<String, T> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, T> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.enable(DeserializationFeature.USE_LONG_FOR_INTS);
        //把类型信息作为属性写入Value
        objectMapper.activateDefaultTyping(objectMapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
        redisTemplate.setKeySerializer(RedisSerializer.string());
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setHashKeySerializer(RedisSerializer.string());
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
    

    jackson官方文档地址

    https://blog.csdn.net/qq_46186167/article/details/123671712

    https://www.tabnine.com/code/java/methods/com.fasterxml.jackson.databind.ObjectMapper/configure

  • 相关阅读:
    [附源码]Python计算机毕业设计java视频点播系统
    Bridge 模式
    电容笔和触控笔哪个好?超实用的ipad电容笔推荐
    【面试八股】IP协议八股
    中止一个或多个 Web 请求
    HIVE 表 DLL 基本操作(一)——第1关:Create/Alter/Drop 数据库
    Apache的配置详解
    Linux下JAVA使用JNA调用C++的动态链接库(g++或者gcc编译的.so文件)
    使用etcd选举sdk实践master/slave故障转移
    Spring整合mybatis和junit
  • 原文地址:https://blog.csdn.net/lemon_TT/article/details/127089893