在前后端拆散时代,服务端跟前端交互的根本数据格式就是json,在Java外面是对象的世界,然而在Java外面是对象的世界,在Spring MVC下,咱们的“return” 进来的对象是如何变成json的呢? 再补充一下,应用Spring MVC,咱们能够间接用对象去接前端传递的参数,那这个转换过程是由谁来实现的呢?这也就是Jackson。本篇咱们仍然采纳问答体来介绍Jackson。
前言
仍旧请出访谈系列的嘉宾小陈、老陈。小陈目前还是实习生,老陈是小陈的领导。某天小陈闲来无事,感觉就有必要看看面试题,忽然收到了领导的微信:
what is Jackson?
电脑背后的小陈挠了挠脑袋,还是学海无涯啊。关上了Jackson在Github上的仓库,看到:
Jackson has been known as "the Java JSON library" or "the best JSON parser for Java". Or simply as "JSON for Java".
Jackson身上最为人所熟知的恐怕是 Java 的JSON库,或者Java中最佳JSON解析者,或者简称为Java的JSON
More than that, Jackson is a suite of data-processing tools for Java (and the JVM platform), including the flagship streaming JSON parser / generator library, matching data-binding library (POJOs to and from JSON) and additional data format modules to process data encoded in Avro, BSON, CSV, Smile, (Java) Properties, Protobuf ,TOML, XML or YAML and even the large set of data format modules to support data types of widely used data types such as Guava, Joda, PCollections and many, many more (see below).
然而Jackson不止于此,Jackson还是一套JVM平台的数据处理工具集: JSON解析与产生,数据绑定(JSON 转 对象,对象JSON),还有解决Avro, BSON, CSV, Smile, (Java) Properties, Protobuf ,TOML, XML or YAML等数据格式等模块,还有大量的数据模块反对更加通用的数据类型解决比方:Guava、Joda Collections 等等。
总结一下就是Jackson不仅仅是一个JSON库,Jackson将本人定位为JVM平台上的数据处理工具,不仅仅是解析JSON,基本上反对了支流的数据格式解析与产生。那我当初最间接的诉求就是JSON到对象,对象到JSON那该怎么实现呢?答案就是ObjectMapper。
对象到JSON
Jackson做数据处理的外围类是ObjectMapper,那ObjectMapper提供了哪些办法来将对象转成JSON呢? 咱们进去找write办法结尾的就行。
public class ObjectMapperDemo { public static void objToJson() throws Exception{ Map<String,Object> stringObjectMap = new HashMap<>(); stringObjectMap.put("name","zs"); stringObjectMap.put("date",new Date()); ObjectMapper objectMapper = new ObjectMapper(); System.out.println(objectMapper.writeValueAsString(stringObjectMap)); } public static void main(String[] args) throws Exception { objToJson(); }}
输入后果:
小陈看了看输入后果,为什么Date字段没变成了工夫戳格局呢? 这是默认的工夫格局吗? 如果是默认的话,有没有方法改呢? 小陈开始搜ObjectMapper的办法, 果然ObjectMapper中就有setDateFormat办法,从新指定一下格局就好了:
public class ObjectMapperDemo { public static void objToJson() throws Exception{ Map<String,Object> stringObjectMap = new HashMap<>(); stringObjectMap.put("name","zs"); stringObjectMap.put("date",new Date()); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); System.out.println(objectMapper.writeValueAsString(stringObjectMap)); } public static void main(String[] args) throws Exception { objToJson(); }}
输入后果:
到这里,小陈忽然想起公司返回给前端的Date类型的字段都变成了yyyy-MM-dd HH:mm:ss,难道是Spring Boot 中的web本人注册的? 于是小陈疾速的搭了一个Spring Boot web我的项目,随便写了一个接口,想验证一下本人的猜测:
@RestControllerpublic class HelloWorldController { @GetMapping("hello") public Map<String,Object> test(String name) { Map<String,Object> map = new HashMap<>(); map.put("aa",new Date()); return map; }}
看着浏览器的后果,小陈想其实公司还是对默认的ObjectMapper进行了革新,应该在我的项目的某个中央,只是本人没看到而已。
JSON到对象
既然对象到JSON就是write结尾的办法,那么我有理由置信,json到对象就是read办法结尾。
依据首个参数类型将其分为9组:
- 首个参数是byte数组
- 首个参数是DataInput
- 首个参数是File对象
- 首个参数是InputStream
- 首个参数是JsonParser
- 首个参数是Reader
- 首个参数是String
- 首个参数是URL
- 首个参数是JsonParser
咱们以首个参数为byte的为例进行钻研,首个参数的类型咱们能够了解为如何将JSON给ObjectMapper。
public class ObjectMapperDemo { private static ObjectMapper objectMapper = new ObjectMapper(); private static void initObjectMapper(){ objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); } public static String objToJson() throws Exception{ Map<String,Object> stringObjectMap = new HashMap<>(); stringObjectMap.put("name","zs"); stringObjectMap.put("date",new Date()); initObjectMapper(); return objectMapper.writeValueAsString(stringObjectMap); } public static void main(String[] args) throws Exception { String json = objToJson(); jsonToObj(json); } private static void jsonToObj(String json) throws Exception { Map<String,Object> map = objectMapper.readValue(json, Map.class); System.out.println(map); }}
那JavaType是用来干什么呢?咱们来看下JavaType:
这是一个抽象类,那看它的子类:
这些类有什么存在意义呢?,这里有没有想到Java的泛型擦除呢?官网称之为类型擦除。JDK 5才引入了泛型,在此之前都是没有泛型的,过后引入泛型的次要目标是参数化类型,然而Java的一大低劣个性就是向前兼容,所以Java在实现泛型的时候抉择了类型擦除,某种程度上你能够了解为Java的泛型没有带到字节码中。那如果我想上面这样做该怎么办:
private static void jsonToObj(String json) throws Exception { // 这里会间接报错 Map<String,Object> map = objectMapper.readValue(json, Map<String,Date>.class); System.out.println(map);}
为了实现下面的成果,Jackson决定曲线救国, 推出了JavaType。那该怎么用呢,咱们用CollectionType为例来介绍:
// 省略get/setpublic class Student { private int id; private String name; private String number; private Boolean status; private Map<String,Object> map;}private static <E> List<E> jsonToObj(String json, Class<? extends List> collectionType ,Class<E> elementType) throws Exception{ CollectionType javaType = TypeFactory.defaultInstance().constructCollectionType(collectionType, elementType); return objectMapper.readValue(json,javaType);}
那CollectionLikeType和MapLikeType是用来做什么的? 这个Like的意思是像,咱们能够在这两个类的正文中一窥端倪:
- CollectionLikeType:
Type that represents things that act similar to Collection; but may or may not be instances of that interface. This specifically allows framework to check for configuration and annotation settings used for Map types, and pass these to custom handlers that may be more familiar with actual type.
这个类型用于解决哪种行为上跟汇合框架类似,但不是汇合框架的实例。特地容许框架去查看用于Map类型的配置和注解,将其传递给更适宜的处理器。
临时还未找到合乎这个要求的汇合
- MapLikeType:
Type that represents Map-like types; things that consist of key/value pairs but that do not necessarily implement Map, but that do not have enough introspection functionality to allow for some level of generic handling. This specifically allows framework to check for configuration and annotation settings used for Map types, and pass these to custom handlers that may be more familiar with actual type.
解决像Map的类型,存储key-value对,然而没有实现Map接口。
到当初来说,JDK内置的汇合框架都是满足咱们要求的,Apache Collections提供了对JDK汇合框架的扩大:
MultiValuedMap 是一个顶层的接口,容许存储key雷同的key-value对
当初咱们来聊聊 TypeReference,这是另一种“避开泛型擦除”的一种形式,正文如是说:
This generic abstract class is used for obtaining full generics type information by sub-classing; it must be converted to ResolvedType implementation (implemented by JavaType from "databind" bundle) to be used. Class is based on ideas from http://gafter.blogspot.com/20... , Additional idea (from a suggestion made in comments of the article) is to require bogus implementation of Comparable (any such generic interface would do, as long as it forces a method with generic type to be implemented). to ensure that a Type argument is indeed given.
Usage is by sub-classing: here is one way to instantiate reference to generic type List<Integer>:TypeReference ref = new TypeReference<List<Integer>>() { };which can be passed to methods that accept TypeReference, or resolved using TypeFactory to obtain
通过TypeReference的子类来取得残缺的泛型信息,这个类的从连贯中的文章失去设计灵感。通过这个类咱们就能够间接将json转换为List<Student>.
应用示例:
studentList = objectMapper.readValue("", new TypeReference<List<Student>>() {});
咱们也来赏析一下这篇文章,这篇文章的作者是赫赫有名的NEAL GAFTER(参加了JDK 1.4 到 5、Lambda表达式的设计),这篇文章简短而又精炼,简略的探讨了如何在泛型擦除下,解决汇合泛型中的泛型,在泛型擦除这种机制下,咱们无奈写出
List<Student>.class
, 这也就是TypeReference的由来,TypeReference是一个抽象类,实例化该类,咱们就能够通过反射获取到填入的泛型信息:
TypeReference<List<String>> x = new TypeReference<List<String>>() {};
下面的参数中咱们不意识的还有JsonParser,从类名上能够推断这个类负责Json解析,类上的正文如是说:
Base class that defines public API for reading JSON content. Instances are created using factory methods of a JsonFactory instance.
定义一组读JSON内容的API,通过JsonFactory来进行实例化。
相对来说外面的办法更为原始,下面的读json,ObjectMapper就是借助JsonParser来做,外面定义了一些读json的底层办法. 本篇文章不做具体介绍,只做理解。
JSON 个性配置
到当初为止,咱们曾经对读写JSON有了一个大抵的理解,那我如果我想配置一些个性呢,比如说为了节俭带宽,字段为null的就不返回给前端该怎么配置呢?
// 在ObjectMapper中配置objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
更多的个性能够在ObjectMapper的configure办法中进行配置, 上面是办法签名:
// JSON 转对象// DeserializationFeature 是一个枚举类// 常见个性为: ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT 这个个性为将空字符串序列化为对象当作null值public ObjectMapper configure(DeserializationFeature f, boolean state)// 对象转JSON public ObjectMapper configure(SerializationFeature f, boolean state)
总结一下
在Jackson中咱们应用read来将json转对象,write来将对象转json,如果咱们须要配置一些个性通过configure进行配置。
参考资料
- Java泛型类型擦除以及类型擦除带来的问题 https://www.cnblogs.com/wuqin...
- Gson-如何解决泛型擦除 https://www.modb.pro/db/140112
- 程序员的福音 - Apache Commons Collections https://zhuanlan.zhihu.com/p/...