乐趣区

关于json:写给小白看的Jackson教程一-基本概念与使用

在前后端拆散时代,服务端跟前端交互的根本数据格式就是 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 我的项目,随便写了一个接口,想验证一下本人的猜测:

@RestController
public 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 组:

  1. 首个参数是 byte 数组
  2. 首个参数是 DataInput
  3. 首个参数是 File 对象
  4. 首个参数是 InputStream
  5. 首个参数是 JsonParser
  6. 首个参数是 Reader
  7. 首个参数是 String
  8. 首个参数是 URL
  9. 首个参数是 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/set
public 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/…
退出移动版