Spring默认使用的JSON工具--Jackson

2023-11-09

Spring默认使用的JSON工具--Jackson

 
一、Jackson介绍

我们常用的json转换工具包括fastJson、Gson、Jackson等。其中Gson是Google所维护,功能全。fastJson特点是快,但是爆出几次的重大bug让人很难再去选择它。fastjson这么快老外为啥还是热衷 jackson? JackSon是Spring的御用工具,和Spring无缝集成,提供灵活的定制化开发的注解。如果使用Spring框架进行开发,建议使用JackSon。

Jackson包含两个不同的解析器:

    Jackson ObjectMapper,将JSON转化为Java对象,或者转换为Jackson特定的树结构
    Jackson JsonParser,JSON流解析器,每次只解析一个JSON标记(暂不做讲解)

Jackson还包含两个不同的生成器:

    Jackson ObjectMapper,可以从Java对象生成JSON,或者是从Jackson的树结构生成JSON
    Jackson Generator,每次生成一个JSON标记(暂不做讲解)

 
二、 ObjectMapper
1.ObjectMapper解析JSON的原理

默认情况下,Jackson通过Java bean的get,set方法,通过去掉get,set再把首字母小写得到的名字去和JSON的属性进行匹配。例如对于getBrand()和setBrand()经过处理得到brand,就会匹配到JSON的brand属性,从而把JSON brand属性的值赋给bean的brand字段。通过一系列这样的处理,就将JSON转换成了Java bean。如果需要以不同的方式来匹配,那么就得使用定制的serializer和deserializer,或者使用Jackson提供的众多的注解。
2.通过ObjectMapper序列化和反序列化

    //car类定义
    public class Car {
       private String brand = null;
       private Integer doors = 0;
       // get set......
    }

反序列化代码:

    String carJson = "{ \"brand\" : \"Mercedes\", \"doors\" : 5 }";
    // 从字符串创建(最常用的方式)
    Car car = objectMapper.readValue(carJson, Car.class);
    System.out.println(objectMapper.writeValueAsString(car));
     
    String jsonArray = "[{\"brand\":\"ford\"}, {\"brand\":\"Fiat\"}]";
    // 解析为数组
    Car[] cars = objectMapper.readValue(jsonArray, Car[].class);
    System.out.println(objectMapper.writeValueAsString(cars));
     
    // 解析为list
    List<Car> carList = objectMapper.readValue(jsonArray, new TypeReference<List<Car>>() {});
    System.out.println(objectMapper.writeValueAsString(carList));
     
    String jsonObjectStr = "{\"brand\":\"ford\", \"doors\":5}";
    // 解析为Map
    Map<String, Object> map = objectMapper.readValue(jsonObjectStr,new TypeReference<Map<String, Object>>() {});
    System.out.println(objectMapper.writeValueAsString(map));
     
    // 从Reader创建
    Reader reader = new StringReader(carJson);
    car = objectMapper.readValue(reader, Car.class);
    System.out.println(objectMapper.writeValueAsString(car));
     
    File file = ResourceUtils.getFile("classpath:car.json");
    // 从文件创建
    car = objectMapper.readValue(file, Car.class);
    System.out.println(objectMapper.writeValueAsString(car));
     
    URL url = ResourceUtils.getFile("classpath:car.json").toURI().toURL();
    // 从URL创建
    car = objectMapper.readValue(url, Car.class);
    System.out.println(objectMapper.writeValueAsString(car));
     
    InputStream input = new FileInputStream(file);
    // 从InputStream创建
    car = objectMapper.readValue(input, Car.class);
    System.out.println(objectMapper.writeValueAsString(car));
     

序列化代码:

    //直接写入文件
    ObjectMapper objectMapper = new ObjectMapper();
    Car car = new Car("yellow", "renault");
    objectMapper.writeValue(new File("target/car.json"), car);
    //output:{"color":"yellow","type":"renault"}
     
    //writeValueAsString和writeValueAsBytes方法通过Java Bean生成String或者byte Array类型的JSON。
    String carAsString = objectMapper.writeValueAsString(car);

3.配置ObjectMapper的工作方式

1.忽略JSON中多余的属性

有时JSON会拥有比Java对象更多的属性,这种情况下Jackson默认会抛出异常,大概意思是说JSON某个属性未知因为在Java bean中未找到该属性。

然而,有时我们的确会遇到JSON属性比Java对象多的情况。例如我们从REST service获取的JSON会包含更多不需要的属性,这种情况下,Jackson允许通过配置来忽略未知的属性,像这样:

objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

更多的API可以点进ObjectMapper类查看每个枚举对应的Javadoc
三、Jackson的使用
1.Jackson对于Date类的处理

默认情况下Date类型属性会被序列化long的毫秒数,即timeStamp那种类型的。然而Jackson也支持将Date序列化成特定格式的日期字符串。

    public class Transaction {
        /**
         * 单独指定序列化后的格式和反序列化时以此格式来解析日期字符串
         */
        @JsonFormat(pattern = "yyyyMMdd HH:mm:ssSSS", locale = "zh_CN", timezone = "GMT+8")
        private Date date = null;
        private Date create = null;
        // get set......
    }

测试代码:

            Transaction transaction = new Transaction();
            transaction.setCreate(new Date());
            transaction.setDate(new Date());
            String resJson = objectMapper.writeValueAsString(transaction);
            // 默认把时间序列化为long
            // {"date":"20190618 11:33:42845","create":1560828822845}
            System.out.println(resJson);

也可以设置ObjectMapper的DateFormat,可以影响所以以该objectMapper对象序列化的所有Java Bean。注意,在Bean中使用注解@JsonFormat定义的格式优先级大于ObjectMapper的DateFormat。

            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.SIMPLIFIED_CHINESE);
            dateFormat.setTimeZone(TimeZone.getTimeZone("GMT+8"));
            // 设置这个会影响所有Bean的Date序列化和反序列化
            objectMapper.setDateFormat(dateFormat);
     
            resJson = objectMapper.writeValueAsString(transaction);
            // {"date":"20190618 11:33:42845","create":"2019-06-18 11:33:42"}
            System.out.println(resJson);
     
            String transactionJsonStr = "{\"type\":\"transfer\",\"date\":\"20190118 14:27:52052\",\"create\":\"2019-01-18 14:27:52\"," +
                    "\"transactionTypeEnum\":\"TICKET\",\"transactionType\":2,\"carTypeEnum\":3}";
            transaction = objectMapper.readValue(transactionJsonStr, Transaction.class);
            System.out.println(transaction);

 2.Jackson的树模型JsonNode

Jackson内置有树模型用来代表JSON对象,这个树模型具体有什么用呢?当你不知道要解析的JSON的结构你就会发现它会很有用,或者由于某种原因你没法创建一个类来表示这个JSON对象。另外一个好处是你可以在使用这个JSON对象之前操纵这个JSON对象,核心类是JsonNode。

            String carJson =
                    "{ \"brand\" : \"Mercedes\", \"doors\" : 5," +
                            " \"owners\" : [\"John\", \"Jack\", \"Jill\"]," +
                            " \"nestedObject\" : { \"field\" : \"value\" } }";
            JsonNode jsonNode = objectMapper.readValue(carJson, JsonNode.class);
            // 或者
            jsonNode = objectMapper.readTree(carJson);
     
            // 取JSON属性值
            JsonNode brandNode = jsonNode.get("brand");
            String brand = brandNode.asText();
            System.out.println("brand = " + brand);
            JsonNode doorsNode = jsonNode.get("doors");
            int doors = doorsNode.asInt();
            System.out.println("doors = " + doors);
     
            // 取JSON数组
            JsonNode jsonArray = jsonNode.get("owners");
            JsonNode jsonArrayNode = jsonArray.get(0);
            String john = jsonArrayNode.asText();
            System.out.println("john = " + john);
     
            // 取JSON内嵌对象
            JsonNode childNode = jsonNode.get("nestedObject");
            JsonNode childField = childNode.get("field");
            String field = childField.asText();
            System.out.println("field = " + field);

JsonNode类是不可变的,意味着我们不能直接创建该类的对象,然而可以使用JsonNode的子类ObjectNode来构建对象:

            // 创建ObjectNode
            ObjectNode objectNode = objectMapper.createObjectNode();
            objectNode.put("brand", "Mercedes");
            objectNode.put("doors", 5);
            ObjectNode nestNode = objectMapper.createObjectNode();
            nestNode.put("field", "value");
            objectNode.set("nestedObject", nestNode);
            System.out.println(objectMapper.writeValueAsString(objectNode));

 3.Jackson注解

主要可分为三类:

读写注解(同时影响序列化和反序列化过程)

    @JsonIgnore  用于需要忽略的属性(不参与序列化和反序列化)
    @JsonIgnoreProperties  用于需要忽略的一系列属性
    @JsonIgnoreType   表明所有用到该注解标注的类的地方都会被忽略
    @JsonAutoDetect   用于检测需要将哪类访问权限的属性参与序列化和反序列化
    @JsonProperty    表明序列化和反序列化时以注解指明的名称为准,即可以修改bean到JSON中的字段名称
    @JsonTypeInfo    
    @JsonSubTypes   这两个主要用于处理多态下反序列化时指定真正的类对象。

读注解(只影响反序列化过程)

    @JsonSetter 用于表明set方法应该匹配的JSON的属性,当Java bean的属性名和JSON的属性名不一致时就可以使用此注解
    @JsonAnySetter  用于指明将所有JSON未知的属性都收集在一起
    @JsonCreator  用于表明Java bean有一个构造方法能够匹配JSON的属性和bean的属性,对于一些没有set方法的bean(不可变对象)来说这个注解是很有用的。
    @JsonDeserialize   用于定制bean属性的反序列化过程

写注解(只影响序列化过程)

    @JsonInclude  用于表明值满足特定条件的属性才会被序列化
    @JsonGetter   用于指明序列化后的属性名而不是用bean的字段名
    @JsonAnyGetter   能够让你使用一个Map作为容器包含任何你想要序列化的属性
    @JsonPropertyOrder   用于指明bean属性序列化后在JSON中的顺序
    @JsonRawValue   用于表明属性值不做任何处理原样输出,String类型的值序列化后会被双引号括住,使用此注解后将不会加上双引号。   主要保证本身就是一个JSON的字符串序列化后不出错。
    @JsonValue   表明Jackson不做序列化,由此注解标注的方法完成序列化工作。一种轻量的定制序列化方式。
    @JsonSerialize  用于定制bean属性的序列化过程

demo请参考:https://zhuanlan.zhihu.com/p/69531219
4.定制类的序列化和反序列化

1.单个属性的定制

如果对于类的少数属性进行定制,可以使用轻量的@JsonSerialize和@JsonDeserialize注解。示例如下:

@JsonSerialize

    public class PersonSerializer {
       private long personId = 0;
       private String name = "John";
       // 这里希望把false序列化成0,true序列化成1
       @JsonSerialize(using = OptimizedBooleanSerializer.class)
       private boolean enabled = false;
       // get set......
    }
     
    public class OptimizedBooleanSerializer extends JsonSerializer<Boolean> {
        @Override
        public void serialize(Boolean aBoolean, JsonGenerator jsonGenerator,
                              SerializerProvider serializerProvider)
                throws IOException {
            if (aBoolean) {
                jsonGenerator.writeNumber(1);
            } else {
                jsonGenerator.writeNumber(0);
            }
        }
    }

 @JsonDeserialize

    public class PersonDeserialize {
       private long id = 0;
       private String name = null;
       // 这里我们想把1映射为true,0映射为false
       @JsonDeserialize(using = OptimizedBooleanDeserializer.class)
       private boolean enabled = false;
       // get set......
    }
     
    public class OptimizedBooleanDeserializer extends JsonDeserializer<Boolean> {
        @Override
        public Boolean deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
            String text = jsonParser.getText();
            if ("0".equals(text)) {
                return false;
            } else {
                return true;
            }
        }
    }

2.完全定制化整个类的序列化和反序列化

      public static void main(String[] args) throws IOException {
        ObjectMapper objectMapper = new ObjectMapper();
        // 完全定制类的序列化和反序列化过程
        SimpleModule carModule = new CarModule();
        // 注册针对这个类型的处理模块
        objectMapper.registerModule(carModule);
        Car car = new Car();
        car.setBrand("BMW");
        car.setDoors(4);
        String json = objectMapper.writeValueAsString(car);
        System.out.println(json);
        car = objectMapper.readValue(json, Car.class);
        System.out.println(objectMapper.writeValueAsString(car));
      }

 

    // 类型处理模块
    public class CarModule extends SimpleModule {
        public CarModule() {
            super(PackageVersion.VERSION);
            addDeserializer(Car.class, new CarDeserializer(Car.class));
            addSerializer(Car.class, new CarSerializer(Car.class));
        }
    }
     
    // 序列化器
    public class CarSerializer extends StdSerializer<Car> {
        private static final long serialVersionUID = 2807109332342106505L;
        public CarSerializer(Class<Car> c) {
            super(c);
        }
        @Override
        public void serialize(Car car, JsonGenerator jsonGenerator,
                              SerializerProvider serializerProvider)
                throws IOException {
            jsonGenerator.writeStartObject();
            if (car.getBrand() != null) {
                jsonGenerator.writeStringField("brand", car.getBrand());
            } else {
                jsonGenerator.writeNullField("brand");
            }
            if (car.getDoors() != null) {
                jsonGenerator.writeNumberField("doors", car.getDoors());
            } else {
                jsonGenerator.writeNullField("doors");
            }
            jsonGenerator.writeEndObject();
        }
    }
     
    // 反序列化器
    public class CarDeserializer extends StdDeserializer<Car> {
        private static final long serialVersionUID = 4977601024588834191L;
        public CarDeserializer(Class<?> c) {
            super(c);
        }
        @Override
        public Car deserialize(JsonParser parser, DeserializationContext deserializer) throws IOException {
            Car car = new Car();
            while (!parser.isClosed()) {
                JsonToken jsonToken = parser.nextToken();
                if (JsonToken.FIELD_NAME == jsonToken) {
                    String fieldName = parser.getCurrentName();
                    parser.nextToken();
                    if ("doors".equals(fieldName)) {
                        car.setDoors(parser.getValueAsInt());
                    } else if ("brand".equals(fieldName)) {
                        car.setBrand(parser.getValueAsString());
                    }
                }
            }
            return car;
        }
    }

需要注意的是这种方式必须将类的所有属性都定制化,否则最终序列化出的结果会缺失没写的那部分。

3.结合SpringMVC的定制化

如果想要在Spring前后台传值中方便的定制化,则可以采用这种方式。

详见:2.在springMVC中的具体应用
四、总结

以上就是Jackson的一些介绍和使用简介。Jackson本身作为Spring框架默认使用的序列化工具类,功能强大社区丰富。本身效率也很高。如果是在开发Spring项目,建议首选。
 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring默认使用的JSON工具--Jackson 的相关文章

随机推荐

  • minist图片多分类问题-单层神经网络

    采用打单层隐藏层 使用TensorFlow框架 构建的分类神经网络 入门级小项目 供深度学习 TensorFlow 初学者参考 usr bin env python coding utf 8 In 导入package 读取数据 import
  • DBeaver——一款替代Navicat的数据库可视化工具

    对于很多开发者来说 Navicat这个软件并不陌生 相信这个彩虹色图标的软件 有效的帮助了你的开发工作 从前上学的时候 我都是用的都是从网上找来的密钥进行破解的 但是一直在寻找有没有一个能找到一个免费的能完美替代Navicat的工具 今天就
  • 2.5mnist手写数字识别之优化算法精讲(百度架构师手把手带你零基础实践深度学习原版笔记系列)

    2 5mnist手写数字识别之优化算法精讲 百度架构师手把手带你零基础实践深度学习原版笔记系列 目录 2 5mnist手写数字识别之优化算法精讲 百度架构师手把手带你零基础实践深度学习原版笔记系列 设置学习率 学习率的主流优化算法 设置学习
  • 机器学习——决策树剪枝

    目录 一 决策树剪枝策略 1 1剪枝目的 1 2剪枝策略 1 3判断决策树泛化性能是否提升的方法 二 预剪枝 prepruning 2 1概述 2 2预剪枝优缺点 2 3代码实现 三 后剪枝 postpruning 3 1概述 3 2后剪枝
  • MinIo 安装及其集成到java中上传下载图片信息

    MinIo 安装及其集成到java中上传下载图片信息 win 下安装 下载链接 https min io download windows 下载好后cmd打开控制台 输入 minio exe server D MinIo data 注 D
  • [轻量级RTSP服务]Linux

    背景 随着国产操作系统的推进 传统行业对Linux平台的呼声和需求越来越大 之前几年 我们发布了Linux平台运营商级的RTSP转RTMP推送模块 RTMP推送模块和RTSP RTMP播放模块 前段时间 有开发者问我们 是不是可以在Linu
  • eclipse中springboot项目如何打包成jar文件

    eclipse中springboot项目如何打包成jar包 前提 第一步 清除项目中之前的打包内容 第二步 打jar包 第三步 测试 4 注意事项 前提 在项目的pom xml中配置相关的内容 其中finalName中内容表示jar war
  • 树莓派mqtt协议连接阿里云物联网平台,手机端获取数据并控制

    树莓派C语言mqtt协议连接阿里云物联网平台 一 阿里云后台配置树莓派设备 阿里云添加链接描述 注册 登录 打开控制台 选择产品与服务 物联网平台 进入后 选择设备管理 产品 点击创建产品 产品名称随便写 品类自定义 直连设备 其他默认 最
  • 【设计模式】建造者模式

    建造者模式 Builder Pattern 使用多个简单的对象一步一步构建成一个复杂的对象 这种类型的设计模式属于创建型模式 它提供了一种创建对象的最佳方式 一个 Builder 类会一步一步构造最终的对象 该 Builder 类是独立于其
  • 【概率论与数理统计】猴博士 笔记 p15-16 一、二维连续型求概率

    一维连续型求概率 题型如下 解题步骤如下 其实就是求积分 举例1的例子 例2 解 例3 解 注意 要把Y变为X计算 且要分类讨论y是否大于0 例4 解 去掉max和min的方法 去掉多余项的方法 假设要求AB两项同时发生的概率 当多余项 A
  • kafka使用_使用多线程增加kafka消费能力

    原创 小姐姐味道 微信公众号ID xjjdog 欢迎分享 转载请保留出处 前提 本例适合那些没有顺序要求的消息主题 kafka通过一系列优化 写入和读取速度能够达到数万条 秒 通过增加分区数量 能够通过部署多个消费者增加并行消费能力 但还是
  • order by产生的 Using temporary的优化

    今天遇到一个慢查询的sql sql如下 EXPLAIN SELECT tas f year tc pk id tas f sex tas f rank score tas f age tas f km five tas f km ten t
  • filter过滤器实现权限访问控制以及同一账号只能登录一台设备

    需求 如题目所意 未登录用户不能浏览访问项目内部的资源 对访问的请求和响应进行拦截 且一个用户只能在一台设备登录 权限访问控制功能可以通过过滤器或者拦截器去实现 在这里我用的是过滤器 过滤器可以过滤全部action请求 拦截器则更有针对性
  • unity通过键盘控制物体移动,大小的缩放

    拖动距离 private float distance 10 缩放量 float scale 0 2f 通过键盘 Q 或者 E 控制物体的缩放 通过键盘 w s a d 控制物体上下左右的移动 private void Update if
  • PWM实现线性调光

    1 PWM调光原理 PWM全称为脉宽调制技术 是通过高精度的计数器对方波的占空比进行编码 就是这个东西 其实很好理解 高电平的时候才会做功 低电平的时候肯定不亮啊 PWM就是调制高电平的占比 其实一般是低电平才有效 因为会外接一个12V的电
  • XFocus Windows Internet 服务器安全配置

    Windows 2003版本区别 1 Windows Server 2003 Standard Edition 标准版 针对中小型企业的核心产品 他也是支持双路处理器 4GB的内存 它除了具备 Windows Server 2003 Web
  • Revit SDK下载地址

    20190325更新 共享了如下sdk REVIT 2014 SDK exe REVIT2015SDK SubscriptionRelease msi REVIT 2016 SDK msi Revit 2017 1 SDK Update O
  • 树莓派OpenWrt SD扩展问题

    树莓派OpenWrt磁盘扩展 1 查看问题 前几天给树莓派4B刷了OpenWrt当做软路由来使用 发现树莓派的SD卡空间没有完全被使用 有一部分未分区 已用大概只有2GB df h查看 2 fdisk 命令查看磁盘 3 按p查看分区情况 发
  • ansible自动化运维工具上部署lnmp架构

    ansible自动化运维工具上部署lnmp架构 ansible安装 通过ansible连接到192 168 228 20配置nginx安装 本地也要安装nginx 步骤略 安装mysql 安装PHP ansible自动化运维工具上部署lnm
  • Spring默认使用的JSON工具--Jackson

    Spring默认使用的JSON工具 Jackson 一 Jackson介绍 我们常用的json转换工具包括fastJson Gson Jackson等 其中Gson是Google所维护 功能全 fastJson特点是快 但是爆出几次的重大b