专栏首页TECH flower除了FastJson,你也应该了解一下Jackson(一)

除了FastJson,你也应该了解一下Jackson(一)

在上月末的时候收到一条关于fastjson安全漏洞的消息,突然想到先前好像已经有好多次这样的事件了(在fastjson上面)。关于安全方面,虽然中枪的机率微小,但是在这个信息越来越复杂的时代,安全性也变得越来越重要,就像DevSecOps的诞生,在软件交付的整个价值流中我们也需要注重安全这方面。当然我们现在不谈关于FastJson的优劣,因为我们本文的目标是让大家了解和掌握Jackson。


概览

Jackson是一个非常流行和高效的基于Java的库,它可以序列化java对象或将java对象映射到JSON,反之亦然。当然除了Jackson,在Java中同类型的优秀的库也有很多,比如:

  • Gson
  • json-io
  • Genson

关于哪一个最好或者哪一个最流行,没有明确的答案。技术的种类繁多,每个人对与不同技术的态度也不一样。言归正传,文章主要还是讨论Jackson的。本文主要讲解我们处理Json中最常见的两个操作:

  • 将Java对象序列化为JSON
  • JSON字符串反序列化为Java对象

引入依赖

由于在Spring/SpringBoot中很多组件已经自带了Jackson库,所以很多情况下不需要手动引入Jackson的依赖。

手动引入依赖:

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

这个依赖关系还将传递地向类路径添加以下库:

  1. jackson-annotations-2.9.8.jar
  2. jackson-core-2.9.8.jar
  3. jackson-databind-2.9.8.jar

JavaObject to Json

ObjectMapper

ObjectMapper是一个映射器(或数据绑定器或编解码器),提供了在Java对象(bean的实例)和JSON之间进行转换的功能。

首先定义一个简单的Java类

public class Car {
    private String color;
    private String type;
    // standard getters setters
}

将Java对象转换成Json

我们使用ObjectMapper的writeValue相关Api来对Java对象进行序列化操作

ObjectMapper objectMapper = new ObjectMapper();
Car car = new Car("blue","c1");
System.out.println(objectMapper.writeValueAsString(car));

此时输出

{"color":"blue","type":"c1"}

更多

ObjectMapper的writeValue相关Api还提供了很多便利的Json序列化操作方法,比如:将对象序列化成Json字节数组的writeValueAsBytes()方法、自定义输出源的writeValue()方法...

ObjectMapper objectMapper = new ObjectMapper();
Car car = new Car("blue","c1");
objectMapper.writeValue(new File("./xxx.txt"),car);

运行上述代码,Java对象的序列化Json将被输出到xxx.txt文件。


Json to JavaObject

将Json String转换成Java Object

ObjectMapper objectMapper = new ObjectMapper();
String json = "{\"color\":\"blue\",\"type\":\"c1\"}";
Car car = objectMapper.readValue(json, Car.class);

readValue()方法也接受其他形式的输入,比如包含JSON字符串的文件:

ObjectMapper objectMapper = new ObjectMapper();
Car car = objectMapper.readValue(new File("./xxx.txt"), Car.class);
System.out.println(car);

JSON to Jackson JsonNode

JsonNode

一个JSON可以被解析成一个JsonNode对象,用来从一个特定的节点检索数据.

使用readTree()方法,我们可以将Json字符串转换成JsonNode

ObjectMapper objectMapper = new ObjectMapper();
String json = "{ \"color\" : \"Black\", \"type\" : \"FIAT\" }";
JsonNode jsonNode = objectMapper.readTree(json);
System.out.println(jsonNode.findValue("type").asText());
// 打印出“FAIT”

JSONArrayString to JavaList

ObjectMapper objectMapper = new ObjectMapper();
String jsonCarArray =
    "[{ \"color\" : \"Black\", \"type\" : \"BMW\" }, { \"color\" : 3. \"Red\", \"type\" : \"FIAT\" }]";
List<Car> listCar = objectMapper.readValue(jsonCarArray, new TypeReference<List<Car>>() {});

JSONString to JavaMap

ObjectMapper objectMapper = new ObjectMapper();
String json = "{ \"color\" : \"Black\", \"type\" : \"BMW\" }";
Map<String, Object> map = objectMapper.readValue(json, new TypeReference<Map<String, Object>>() {
});

?:<u>Jackson库最大的优点之一是高度可定制的序列化和反序列化过程。接下来将介绍一些高级特性,其中输入或输出JSON响应可以与生成或使用响应的对象不同。</u>

配置序列化和反序列化特性

String jsonString = "{ \"color\" : \"Black\", \"type\" : \"Fiat\", \"year\" :\"1970\" }";

假设使用如上json字符串来反序列化成Java对象,按照默认解析过程将导致UnrecognizedPropertyException异常,因为其中存在Car类中未包含的新字段year。

通过配置序列化和反序列化特性来解决此问题:

ObjectMapper objectMapper = new ObjectMapper();
String jsonString = "{ \"color\" : \"Black\", \"type\" : \"Fiat\", \"year\" :\"1970\" }";
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Car car = objectMapper.readValue(jsonString, Car.class);

如上,我们在ObjectMapper中配置了DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES=false,从而实现忽略新的字段。

**类似:**另一个选项FAIL_ON_NULL_FOR_PRIMITIVES,它定义了是否允许原始值的空值;FAIL_ON_NUMBERS_FOR_ENUM控制是否允许enum值被序列化/反序列化为数字......


自定义序列化器或反序列化器

自定义序列化器

public static class CustomCarSerializer extends StdSerializer<Car> {
    public CustomCarSerializer() {
        this(null);
    }

    public CustomCarSerializer(Class<Car> t) {
        super(t);
    }

    @Override
    public void serialize(Car car, JsonGenerator jsonGenerator, SerializerProvider serializer) throws IOException {
        jsonGenerator.writeStartObject();
        jsonGenerator.writeStringField("car_brand", car.getType());
        jsonGenerator.writeEndObject();
    }
}

如上,通过继承StdSerializer类,我们实现了一个自定义的序列化器。

使用自定义的序列化器:

ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule("CustomCarSerializer", new Version(1, 0, 0, null, null, null));
module.addSerializer(Car.class, new CustomCarSerializer());
mapper.registerModule(module);
Car car = new Car("yellow", "enault");
System.out.println(mapper.writeValueAsString(car));
//输出{"car_brand":"enault"}

自定义反序列化器

public static class CustomCarDeserializer extends StdDeserializer<Car> {

        public CustomCarDeserializer() {
            this(null);
        }

        protected CustomCarDeserializer(Class<?> vc) {
            super(vc);
        }

        @Override
        public Car deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
            Car car = new Car();
            ObjectCodec codec = p.getCodec();
            JsonNode node = codec.readTree(p);
            // try catch block
            JsonNode colorNode = node.get("color");
            String color = colorNode.asText();
            car.setColor(color);
            return car;
        }
    }

如上,通过继承StdDeserializer类,我们实现了一个自定义的序列化器。

使用自定义的反序列化器:

String json = "{ \"color\" : \"Black\", \"type\" : \"BMW\"}";
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule("CustomCarDeserializer", new Version(1, 0, 0, null, null, null));
module.addDeserializer(Car.class, new CustomCarDeserializer());
mapper.registerModule(module);
Car car = mapper.readValue(json, Car.class);
//此时的car {color='Black', type='null'}

处理时间格式

⚠️:此处仅展示对于Java8的LocalDate&LocalDateTime的处理

首先创建一个带日期时间字段的Car类

public class Car {
    private String color;
    private String type;
  	@JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDateTime produceTime;
    // standard getters setters
}

自定义时间格式处理

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.findAndRegisterModules();
Car car = new Car().setColor("blue").setType("c1").setProduceTime(LocalDateTime.now());
String carAsString = objectMapper.writeValueAsString(car);
System.out.println(carAsString);
//此时输出:{"color":"blue","type":"c1","produceTime":"2020-06-06"}

处理集合

DeserializationFeature类提供的另一个小但有用的特性是能够从JSON数组响应生成我们想要的集合类型。

String jsonCarArray = "[{ \"color\" : \"Black\", \"type\" : \"BMW\"}, { \"color\" : \"Red\", \"type\" : \"FIAT\"}]";
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY, true);
Car[] cars = objectMapper.readValue(jsonCarArray, Car[].class);

如上,我们将一个JsonArray字符串转换成了对象数组。

我们也可以将其转换成集合:

String jsonCarArray = "[{ \"color\" : \"Black\", \"type\" : \"BMW\"}, { \"color\" : \"Red\", \"type\" : \"FIAT\"}]";
ObjectMapper objectMapper = new ObjectMapper();
List<Car> listCar = objectMapper.readValue(jsonCarArray, new TypeReference<List<Car>>(){});

总结

Jackson是一个可靠而成熟的用于Java的JSON序列化/反序列化库。ObjectMapper API提供了一种简单的方法来解析和生成JSON响应对象,具有很大的灵活性。

本文分享自微信公众号 - TECH flower(tech-flower),作者:东溪陈姓少年

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-06-10

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 除了FastJson,你也应该了解一下Jackson(二)

    上一篇文章介绍了Jackson中的映射器ObjectMapper,以及如何使用它来实现Json与Java对象之间的序列化和反序列化,最后介绍了Jackson中一...

    东溪陈姓少年
  • Fastjson到了说再见的时候了

    各位小伙伴大家好,我是A哥。停更1个月后回归啦,今天咱们聊聊一个比较有意思的话题:是否真的需要跟Fastjson说再见了?

    YourBatman
  • 用了几年的 Fastjson,我最终替换成了Jackson!

    作者:larva-zhh 来源:www.cnblogs.com/larva-zhh/p/11544317.html

    Java技术栈
  • [think in spring]spring mvc为什么默认使用jackson而不选择fastjson

    阿里的一些开源项目例如dubbo, druid, fastjson等在国内的影响力是蛮大的。今天谈下温少的fastjson, 它的流行源于它的快, 参考作者的谈...

    Zeal
  • 用了几年的 Fastjson,最终替换成了 Jackson!

    > 公众号:[Java小咖秀](https://t.1yb.co/jwkk),网站:[javaxks.com](https://www.javaxks.com)

    Java小咖秀
  • 1. 初识Jackson -- 世界上最好的JSON库

    各位小伙伴大家好,我是A哥。上篇文章 【Fastjson到了说再见的时候了】 A哥跟Fastjson说了拜拜,从本系列开始,我们将一起进入Jackson库的学习...

    YourBatman
  • Fastjson与Jackson性能问题

    话说在前面,本篇并不会去比较fastjson与Jackson在正常业务场景下的json解析性能,侧重点是比较两者在解析大json字符串上的性能。可以给出的结论是...

    Java艺术
  • 1. 初识Jackson -- 世界上最好的JSON库

    各位小伙伴大家好,我是A哥。上篇文章 【Fastjson到了说再见的时候了】 A哥跟Fastjson说了拜拜,从本系列开始,我们将一起进入Jackson库的学习...

    YourBatman
  • 1. 初识Jackson -- 世界上最好的JSON库

    各位小伙伴大家好,我是A哥。上篇文章 【Fastjson到了说再见的时候了】 A哥跟Fastjson说了拜拜,从本系列开始,我们将一起进入Jackson库的学习...

    YourBatman
  • 为什么 FastJson 火不起来,国外人都不使用?

    首先抄录一段来自官网的介绍:FastJson是阿里巴巴的开源JSON解析库,它可以解析JSON格式的字符串,支持将Java Bean序列化为JSON字符串,也可...

    业余草
  • 弃坑 FastJson,不香了

    首先抄录一段来自官网的介绍:FastJson是阿里巴巴的开源JSON解析库,它可以解析JSON格式的字符串,支持将Java Bean序列化为JSON字符串,也可...

    程序员泥瓦匠
  • 浅析 SpringMVC 中返回对象的循环引用问题

    「技术分享」某种程度上,是让作者和读者,不那么孤独的东西。欢迎关注我的微信公众号:「Kirito的技术分享」

    kirito-moe
  • 走你!fastjson!

    首先抄录一段来自官网的介绍:FastJson是阿里巴巴的开源JSON解析库,它可以解析JSON格式的字符串,支持将Java Bean序列化为JSON字符串,也可...

    Java旅途
  • 大型项目废弃fastjson迁移至Gson保姆级攻略

    在被大家取关之前,我立下一个“远大的理想”,一定要在这周更新文章。现在看来,flag有用了。。。

    蛮三刀酱
  • 为什么我们公司强制弃坑FastJson了?主推Jackson~

    作者:咔咔侃技术 来自:toutiao.com/i6815906868183958027

    良月柒
  • 都是微服务的天下了,还有不知道 JSON 的程序员吗?

      JSON(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式。它基于 ECMAScript (欧洲计算机协...

    Demo_Null
  • 重学springboot系列之JSON处理工具类

    fastjson为人诟病的问题:虽然fastjson速度上有一定的优势,但是其为了追求速度,很大程度放弃了JSON的规范性。因此还时不时的在有些版本中暴露安全问...

    大忽悠爱学习
  • 知乎:fastjson 这么快,为啥老外还是热衷 jackson?

    知乎上有人提问:fastjson这么快,为啥老外还是热衷 jackson? 下面是甘明的回答,从各个方面分析了这个问题:

    芋道源码
  • SpringBoot 返回 json 数据以及数据封装(万字长文)

    在项目开发中,接口与接口之间,前后端之间数据的传输都使用 Json 格式,在 Spring Boot 中,接口返回 Json 格式的数据很简单,在 Contro...

    技术从心

扫码关注云+社区

领取腾讯云代金券