一文读懂Json序列化与反序列化

一文读懂Json序列化与反序列化

#mermaid-svg-tVjnnlFu6ZBDpGOQ {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-tVjnnlFu6ZBDpGOQ .error-icon{fill:#552222;}#mermaid-svg-tVjnnlFu6ZBDpGOQ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-tVjnnlFu6ZBDpGOQ .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-tVjnnlFu6ZBDpGOQ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-tVjnnlFu6ZBDpGOQ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-tVjnnlFu6ZBDpGOQ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-tVjnnlFu6ZBDpGOQ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-tVjnnlFu6ZBDpGOQ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-tVjnnlFu6ZBDpGOQ .marker.cross{stroke:#333333;}#mermaid-svg-tVjnnlFu6ZBDpGOQ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-tVjnnlFu6ZBDpGOQ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-tVjnnlFu6ZBDpGOQ .cluster-label text{fill:#333;}#mermaid-svg-tVjnnlFu6ZBDpGOQ .cluster-label span{color:#333;}#mermaid-svg-tVjnnlFu6ZBDpGOQ .label text,#mermaid-svg-tVjnnlFu6ZBDpGOQ span{fill:#333;color:#333;}#mermaid-svg-tVjnnlFu6ZBDpGOQ .node rect,#mermaid-svg-tVjnnlFu6ZBDpGOQ .node circle,#mermaid-svg-tVjnnlFu6ZBDpGOQ .node ellipse,#mermaid-svg-tVjnnlFu6ZBDpGOQ .node polygon,#mermaid-svg-tVjnnlFu6ZBDpGOQ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-tVjnnlFu6ZBDpGOQ .node .label{text-align:center;}#mermaid-svg-tVjnnlFu6ZBDpGOQ .node.clickable{cursor:pointer;}#mermaid-svg-tVjnnlFu6ZBDpGOQ .arrowheadPath{fill:#333333;}#mermaid-svg-tVjnnlFu6ZBDpGOQ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-tVjnnlFu6ZBDpGOQ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-tVjnnlFu6ZBDpGOQ .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-tVjnnlFu6ZBDpGOQ .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-tVjnnlFu6ZBDpGOQ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-tVjnnlFu6ZBDpGOQ .cluster text{fill:#333;}#mermaid-svg-tVjnnlFu6ZBDpGOQ .cluster span{color:#333;}#mermaid-svg-tVjnnlFu6ZBDpGOQ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-tVjnnlFu6ZBDpGOQ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

Json
Json:轻量级数据交换格式
Json与Java
Json与SpringWeb
人:易读写(文本格式)
开发:易解析转换(kv结构)
传输:轻量省流量(对比xml格式)
序列化(Object -> Json)
反序列化(Json -> Object)
HttpMessageConverter & RequestResponseBodyMethodProcessor
AbstractJackson2HttpMessageConverter & AbstractJsonHttpMessageConverter
ObjectMapper...

通常我们在Web开发中,都使用Json来传输和交换数据。今天我们来揭开Json神秘的面纱,看一看:

  1. Json5wwhywhatwhowhenwhere
  2. Java开发中,如何将Java对象序列化成Json和如何将Json反序列化成Java对象
  3. SpringBoot开发Web项目时,Json如何在Http协议和SpringWeb框架中发挥作用

最后,我们再补充一点在实际项目中可行的一些实践。

Json5w

  1. Json是什么? -> 一种可以表示kv键值对、array集合、value三种结构的文本数据格式,key是**String**,value可以是任意数据类型

     {"k-str":"a string","k-num":100,"key-null":null,"key-object":{"id":1234,"name":"a object"},"k-list":[1,2,3,4,5],"k-map":{"kv-k1":"k1","kv-k2":2,"kv-k3":null}}
    
  2. 为什么用Json? -> 结构简单,易于读写,便于传输

  3. Json用于哪些场景?数据传输交换

Json序列化与反序列化

补充一个关于反序列化的一个有意思的小知识。反序列化产生一个Java对象,而在java中,对象的创建有2种方式:

  1. 通过构造器new一个
  2. clone一个已存在的实例

前者通过构造器创建,会默认执行构造器中定义的初始化逻辑,后者通过内存复制不会走初始化方法但需要实现Cloneable接口。那么Json反序列化使用的是哪种方式呢?是第一种啦!所以不要在构造器中埋坑哦~

#mermaid-svg-j3M3sHjKTQnjTVQ3 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-j3M3sHjKTQnjTVQ3 .error-icon{fill:#552222;}#mermaid-svg-j3M3sHjKTQnjTVQ3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-j3M3sHjKTQnjTVQ3 .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-j3M3sHjKTQnjTVQ3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-j3M3sHjKTQnjTVQ3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-j3M3sHjKTQnjTVQ3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-j3M3sHjKTQnjTVQ3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-j3M3sHjKTQnjTVQ3 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-j3M3sHjKTQnjTVQ3 .marker.cross{stroke:#333333;}#mermaid-svg-j3M3sHjKTQnjTVQ3 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-j3M3sHjKTQnjTVQ3 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-j3M3sHjKTQnjTVQ3 .cluster-label text{fill:#333;}#mermaid-svg-j3M3sHjKTQnjTVQ3 .cluster-label span{color:#333;}#mermaid-svg-j3M3sHjKTQnjTVQ3 .label text,#mermaid-svg-j3M3sHjKTQnjTVQ3 span{fill:#333;color:#333;}#mermaid-svg-j3M3sHjKTQnjTVQ3 .node rect,#mermaid-svg-j3M3sHjKTQnjTVQ3 .node circle,#mermaid-svg-j3M3sHjKTQnjTVQ3 .node ellipse,#mermaid-svg-j3M3sHjKTQnjTVQ3 .node polygon,#mermaid-svg-j3M3sHjKTQnjTVQ3 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-j3M3sHjKTQnjTVQ3 .node .label{text-align:center;}#mermaid-svg-j3M3sHjKTQnjTVQ3 .node.clickable{cursor:pointer;}#mermaid-svg-j3M3sHjKTQnjTVQ3 .arrowheadPath{fill:#333333;}#mermaid-svg-j3M3sHjKTQnjTVQ3 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-j3M3sHjKTQnjTVQ3 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-j3M3sHjKTQnjTVQ3 .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-j3M3sHjKTQnjTVQ3 .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-j3M3sHjKTQnjTVQ3 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-j3M3sHjKTQnjTVQ3 .cluster text{fill:#333;}#mermaid-svg-j3M3sHjKTQnjTVQ3 .cluster span{color:#333;}#mermaid-svg-j3M3sHjKTQnjTVQ3 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-j3M3sHjKTQnjTVQ3 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

Json数据类型与Java数据类型映射
序列化
反序列化
数值: 123
布尔:true/false
字符串: a
集合:[1,2]
对象:{}
Jdk支持的单个对象类型:基本(包装)类型、枚举、String、日期等...
集合、数组
自定义对象、Map
Java <-> Json
Java对象 -> Json
Json -> Java

这里特指的是在Java中,Java对象序列化成Json文本和与Json文本反序列化成Java对象。

上面我们已经看到,Json其实就是一种kv结构,如果把Java对象的属性名作为k,属性值作为v,那Json和我们的Java对象结构是相似的。

而对于集合/数组和Map类型呢?

Json同样支持集合类型,对于Map天然的就是kv结构,直接将Map对象的key作为Jsonk,将Mapvalue作为Jsonv即可。这就是**Java序列化成Json**,反过来则是反序列化。

那当然对于Map需要注意的一点是,Jsonk只支持String类型,而Mapkey可以是任意对象,所以通常会使用toString()Mapkey转成Jsonk

另外,对于Jdk自带的如Jsr310的日期类型等,通常根据Json工具的支持会序列化成Json的数值或字符串。我们自定义的类型则直接序列化成Json的对象。

我们就不班门弄斧,直接使用一些Json工具来实例一下:

 package com.gitee.theskyone.bird.web;import com.fasterxml.jackson.core.JsonProcessingException;import com.fasterxml.jackson.databind.ObjectMapper;import com.google.common.collect.Lists;import lombok.Data;import java.util.Arrays;import java.util.List;import java.util.Map;import java.util.stream.Collectors;/*** @author theskyzero* @date 2022-04-16*/public class JsonDemoTest {@Datastatic class JsonDemo {Long id = 10L;String name = "demo";int[] ints = new int[]{1, 2, 3};Double[] doubles = new Double[]{4.1, 4.2, 4.33};List<Byte[]> bytesList = Arrays.asList(new Byte[]{5, 6, 7}, new Byte[]{8, 9});List<Byte>[] byteLists = new List[]{Lists.newArrayList(3, 4, 5), Lists.newArrayList(6, 7, 8)};Map<String, List<Long>> stringListMap = Lists.newArrayList(2L, 34L, 5L).stream().collect(Collectors.groupingBy(id -> id + ""));Map<JsonDemo, List<String>> jsonDemoMap = Lists.newArrayList("string").stream().collect(Collectors.groupingBy(id -> this));@Overridepublic String toString() {return "JsonDemo{" +"id=" + id +", name='" + name + '\\'' +'}';}}public static void main(String[] args) throws JsonProcessingException {JsonDemo jsonDemo = new JsonDemo();ObjectMapper mapper = new ObjectMapper();// 序列化String serialized = mapper.writeValueAsString(jsonDemo);System.out.println(serialized);// 反序列化JsonDemo readValue = mapper.readValue(serialized, JsonDemo.class);System.out.println(readValue);}}// 输出{"id":10,"name":"demo","ints":[1,2,3],"doubles":[4.1,4.2,4.33],"bytesList":[[5,6,7],[8,9]],"byteLists":[[3,4,5],[6,7,8]],"stringListMap":{"34":[34],"2":[2],"5":[5]},"jsonDemoMap":{"JsonDemo{id=10, name='demo'}":["string"]}}Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot find a (Map) Key deserializer for type [simple type, class com.gitee.theskyone.bird.web.JsonDemoTest$JsonDemo]at [Source: (String)"{"id":10,"name":"demo","ints":[1,2,3],"doubles":[4.1,4.2,4.33],"bytesList":[[5,6,7],[8,9]],"byteLists":[[3,4,5],[6,7,8]],"stringListMap":{"34":[34],"2":[2],"5":[5]},"jsonDemoMap":{"JsonDemo{id=10, name='demo'}":["string"]}}"; line: 1, column: 1]at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1904)at com.fasterxml.jackson.databind.deser.DeserializerCache._handleUnknownKeyDeserializer(DeserializerCache.java:603)at com.fasterxml.jackson.databind.deser.DeserializerCache.findKeyDeserializer(DeserializerCache.java:168)at com.fasterxml.jackson.databind.DeserializationContext.findKeyDeserializer(DeserializationContext.java:669)at com.fasterxml.jackson.databind.deser.std.MapDeserializer.createContextual(MapDeserializer.java:302)at com.fasterxml.jackson.databind.DeserializationContext.handlePrimaryContextualization(DeserializationContext.java:825)at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.resolve(BeanDeserializerBase.java:550)at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:294)at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:244)at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:142)at com.fasterxml.jackson.databind.DeserializationContext.findRootValueDeserializer(DeserializationContext.java:642)at com.fasterxml.jackson.databind.ObjectMapper._findRootDeserializer(ObjectMapper.java:4805)at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4675)at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3629)at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3597)at com.gitee.theskyone.bird.web.JsonDemoTest.main(JsonDemoTest.java:45)

翻车了是不是,我们序列化正常,反序列化失败,为什么?很简单,我们序列化出来的jsonDemoMapkeyJsonDemotoString(),不是个Json格式,无法反序列化出来JsonDemo作为我们JsonDemo字段的key。所以我们在使用Map时要注意在Json反序列化时的影响。Map其实不建议在我们正常的编码中去使用的,正常的业务我们会定义为对象。即使类似变化的kv结构元数据配置也可以转换成集合,因为Map<K,V> = List<Map.Entry<K,V>>

Json的序列化反序列化就到这里,并不复杂,我们只要知道它就是在Java对象和Json文本间相互转换就行。那么剩下的无非是利用各种Json工具来实现这个相互转换功能,一般常用的有JacksonFastJSONGson等,原理和功能都大同小异。

Jackson

SpringBoot默认使用Jackson,这里我们通过Jackson来瞅一瞅一般使用Json的姿势吧~

最简单序列化/反序列化工具类

使用了默认的ObjectMapper,可以满足我们一般的需要,但是对于Jsr310时间类型不支持,对于集合/泛型也不支持。

 public class JsonUtils {private static final ObjectMapper MAPPER = new ObjectMapper();public static <T> String serialize(T data) {try {return MAPPER.writeValueAsString(data);} catch (JsonProcessingException e) {throw new RuntimeException(e);}}public static <T> T deserialize(String json, Class<T> clazz) {try {return MAPPER.readValue(json, clazz);} catch (JsonProcessingException e) {throw new RuntimeException(e);}}}

最佳实践1: 支持泛型和Jdk8类型的工具类

  1. 通过spi扫描注册默认支持的modules
  2. 添加一个重载方法,使用TypeReference支持泛型反序列化
 public class JsonUtils {private static ObjectMapper MAPPER = new ObjectMapper();static {// 通过spi注册支持的modules(对象映射器),如JavaTimeModule等MAPPER.findAndRegisterModules();}public static <T> String serialize(T data) {try {return MAPPER.writeValueAsString(data);} catch (JsonProcessingException e) {throw new RuntimeException(e);}}public static <T> T deserialize(String json, Class<T> clazz) {try {return MAPPER.readValue(json, clazz);} catch (JsonProcessingException e) {throw new RuntimeException(e);}}public static <T> T deserialize(String json, TypeReference<T> clazz) {try {return MAPPER.readValue(json, clazz);} catch (JsonProcessingException e) {throw new RuntimeException(e);}}}

这个工具类基本上已经能满足日常需要了。之后的是一些个性化的配置特性及功能。

最佳实践2:序列化/反序列化特性

忽略错误

忽略Json数据和Java对象不一致产生的错误。

 public class JsonUtils {private static ObjectMapper MAPPER = new ObjectMapper();static {// 通过spi注册支持的modules(对象映射器),如JavaTimeModule等MAPPER.findAndRegisterModules();// 忽略序列化异常MAPPER.disable(// 忽略比如使用new Object()作为返回值无法序列化的错误 -> 写这样的代码不会被打吗?SerializationFeature.FAIL_ON_EMPTY_BEANS,// 忽略get返回this的错误SerializationFeature.FAIL_ON_SELF_REFERENCES);// 忽略反序列化异常MAPPER.disable(// 忽略Json中存在Java对象木有的字段DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);}}

时间处理

时间类型交互时可以考虑使用时间戳。另外,关于日期类型,还可以结合使用@JsonFormat做一些定制。

 public class JsonUtils {private static ObjectMapper MAPPER = new ObjectMapper();static {// 通过spi注册支持的modules(对象映射器),如JavaTimeModule等MAPPER.findAndRegisterModules();// 日期以时间戳序列化MAPPER.enable(// date以时间戳序列化,单独列出是因为springboot默认给禁用了SerializationFeature.WRITE_DATES_AS_TIMESTAMPS).disable(// 禁用写 nanosSerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS// 使用系统默认时区).setTimeZone(TimeZone.getDefault());}}

枚举处理

枚举默认使用name()序列化成String。可以调整使用toString(),可以简单的支持自定义枚举类型序列化。

 public class JsonUtils {private static ObjectMapper MAPPER = new ObjectMapper();static {// 通过spi注册支持的modules(对象映射器),如JavaTimeModule等MAPPER.findAndRegisterModules();// 枚举处理MAPPER.enable(// 使用toString()序列化SerializationFeature.WRITE_ENUMS_USING_TO_STRING).enable(// 使用toString()反序列化DeserializationFeature.READ_ENUMS_USING_TO_STRING);}}

空值处理

Jackson默认会序列化所有字段,对于空值我们可以设置不序列化,减少数据传输等。或者比如使用spring-cache分布式缓存,也可以减少缓存存储。

 public class JsonUtils {private static ObjectMapper MAPPER = new ObjectMapper();static {// 通过spi注册支持的modules(对象映射器),如JavaTimeModule等MAPPER.findAndRegisterModules();// 仅序列化非NULL字段 -> NON_EMPTY和NON_DEFAULT杀伤力更大,NON_NULL符合一般实践认知MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL);}}

忽略大小写

 public class JsonUtils {private static ObjectMapper MAPPER = new ObjectMapper();static {// 通过spi注册支持的modules(对象映射器),如JavaTimeModule等MAPPER.findAndRegisterModules();// 大小写不敏感MAPPER.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES);}}

最佳实践3:项目实战特性

一些适合在项目中玩转的功能特性。

字段映射

默认情况下,我们推荐Java序列化成Json的字段名使用驼峰格式,不过有些外部系统可能设计不同或概念差异,字段名格式或名称与我们系统内部定义不一样,这时候我们通常会做字段映射,不必将外部系统的差异引入到系统内部。

使用@JsonProperties@JsonAlias指定Json字段名称。顾名思义,前者改写(反)序列化时的字段名称,后者是别名,在反序列化时生效。

 public class JsonDemo {public static void main(String[] args) {A a = new A();// 序列化成a_instantString serialize = JsonUtils.serialize(a);System.out.println(serialize);// bDate别名可被解析serialize = "{\\"bDate\\":1650116489517,\\"localDateTime\\":[2022,4,16,21,41,29,522],\\"a_instant\\":1650116489517}";A deserialize = JsonUtils.deserialize(serialize, A.class);System.out.println(deserialize);}@Datastatic class A {@JsonProperty("a_instant")Instant a = Instant.now();@JsonAlias("bDate")Date b = new Date();LocalDateTime localDateTime = LocalDateTime.now();}}// 输出{"b":1650116519832,"localDateTime":[2022,4,16,21,41,59,837],"a_instant":1650116519832}JsonUtils.A(a=+54260-02-10T11:31:57Z, b=Sat Apr 16 21:41:29 CST 2022, localDateTime=2022-04-16T21:41:29.000000522)

格式化输出

这个功能在数据传输的场景不实用,后端开发通常也不会关心Json数据的样式。了解即可。

 public class JsonUtils {private static final ObjectMapper MAPPER = new ObjectMapper();public static <T> String formatSerialize(T data) {try {return MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(data);} catch (JsonProcessingException e) {throw new RuntimeException(e);}}}

忽略字段

有一些特殊的场景,比如我们对象中有些方法或字段仅系统内部使用,不需要提供外部。或者更多的比如创建请求和更新请求,更新必须携带id而创建则不需要。这些场景下需要我们在序列化/反序列化时忽略某些不需要关心、暴露的字段。

这部分已经比较深入使用Jackson了,还是更建议使用不同的对象来区分不同的使用场景。简单的场景下我们可以使用@JsonIgnore@JsonignoreProperties来静态忽略字段。而对于创建和更新请求这个场景,简单一点可以在业务处理上忽略创建请求携带的id,复杂点的实现则要通过@JsonView@JsonFilter来动态处理。

 public class JsonUtils {private static final ObjectMapper MAPPER = new ObjectMapper();public static <T, V> String serializeWithView(T data, Class<V> view) {try {return MAPPER.writerWithView(view).writeValueAsString(data);} catch (JsonProcessingException e) {throw new RuntimeException(e);}}public static <T, V> T deSerializeWithView(String data, Class<T> clazz, Class<V> view) {try {return MAPPER.readerWithView(view).readValue(data, clazz);} catch (JsonProcessingException e) {throw new RuntimeException(e);}}}

深拷贝

我们可以借助JSON来实现对象深拷贝,即序列化+反序列化。

 public class JsonUtils {private static final ObjectMapper MAPPER = new ObjectMapper();public static <S, T> T convert(S source, Class<T> clazz) {return MAPPER.convertValue(source, clazz);}public static <S, T> T convert(S source, TypeReference<T> typeReference) {return MAPPER.convertValue(source, typeReference);}}

SpringWeb中的Json应用

SpringMvc中如何使用处理application/**+json?

SpringMVC请求处理流程

#mermaid-svg-9cS72Sz1TAEewHRP {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-9cS72Sz1TAEewHRP .error-icon{fill:#552222;}#mermaid-svg-9cS72Sz1TAEewHRP .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-9cS72Sz1TAEewHRP .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-9cS72Sz1TAEewHRP .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-9cS72Sz1TAEewHRP .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-9cS72Sz1TAEewHRP .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-9cS72Sz1TAEewHRP .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-9cS72Sz1TAEewHRP .marker{fill:#333333;stroke:#333333;}#mermaid-svg-9cS72Sz1TAEewHRP .marker.cross{stroke:#333333;}#mermaid-svg-9cS72Sz1TAEewHRP svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-9cS72Sz1TAEewHRP .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-9cS72Sz1TAEewHRP text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-9cS72Sz1TAEewHRP .actor-line{stroke:grey;}#mermaid-svg-9cS72Sz1TAEewHRP .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-9cS72Sz1TAEewHRP .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-9cS72Sz1TAEewHRP #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-9cS72Sz1TAEewHRP .sequenceNumber{fill:white;}#mermaid-svg-9cS72Sz1TAEewHRP #sequencenumber{fill:#333;}#mermaid-svg-9cS72Sz1TAEewHRP #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-9cS72Sz1TAEewHRP .messageText{fill:#333;stroke:#333;}#mermaid-svg-9cS72Sz1TAEewHRP .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-9cS72Sz1TAEewHRP .labelText,#mermaid-svg-9cS72Sz1TAEewHRP .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-9cS72Sz1TAEewHRP .loopText,#mermaid-svg-9cS72Sz1TAEewHRP .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-9cS72Sz1TAEewHRP .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-9cS72Sz1TAEewHRP .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-9cS72Sz1TAEewHRP .noteText,#mermaid-svg-9cS72Sz1TAEewHRP .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-9cS72Sz1TAEewHRP .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-9cS72Sz1TAEewHRP .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-9cS72Sz1TAEewHRP .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-9cS72Sz1TAEewHRP .actorPopupMenu{position:absolute;}#mermaid-svg-9cS72Sz1TAEewHRP .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-9cS72Sz1TAEewHRP .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-9cS72Sz1TAEewHRP .actor-man circle,#mermaid-svg-9cS72Sz1TAEewHRP line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-9cS72Sz1TAEewHRP :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}tomcatDispatcherServletHanlderMappingHandlerAdapterHttpRequestdoDispatch()HandlerMapping.getHandler()RequestMappingHandlerMapping.getHandlerInternal()HandlerExecutionChaingetHandlerAdaptorHandlerExecutionChain.applyPreHandle()HandlerAdapter.handle()RequestMappingHandlerAdapter.handleInternal()在这里解析请求、处理响应:(反)序列化Java对象ModelAndViewHandlerExecutionChain.applyPostHandle()ModelAndView.getView().render()HttpResponsetomcatDispatcherServletHanlderMappingHandlerAdapterSpringMvc处理流程

一般即在RequestMappingHandlerMapping中解析请求、处理响应。我们可以在RequestMappingHandlerMapping中看到很多解析器来处理Http消息,其中RequestResponseBodyMethodProcessor即用来处理@RequestBody@ResponseBody。而处理又是通过各种HttpMessageConverter来完成HttpMessageJava对象的相互转换。对于application/**+json默认使用MappingJackson2HttpMessageConverter

MappingJackson2HttpMessageConverter

MappingJackson2HttpMessageConverter中即使用了JacksonObjectMapper。那我们这里其实就是看在MappingJackson2HttpMessageConverterObjectMapper的使用姿势。其中到底用到了Jackson哪些玩法呢?

package org.springframework.http.converter.json;public abstract class AbstractJackson2HttpMessageConverter extends AbstractGenericHttpMessageConverter<Object> {protected AbstractJackson2HttpMessageConverter(ObjectMapper objectMapper) {this.objectMapper = objectMapper;setDefaultCharset(DEFAULT_CHARSET);DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter();prettyPrinter.indentObjectsWith(new DefaultIndenter("  ", "\ndata:"));this.ssePrettyPrinter = prettyPrinter;}/** * Whether to use the {@link DefaultPrettyPrinter} when writing JSON.* This is a shortcut for setting up an {@code ObjectMapper} as follows:* <pre class="code">* ObjectMapper mapper = new ObjectMapper();* mapper.configure(SerializationFeature.INDENT_OUTPUT, true);* converter.setObjectMapper(mapper);* </pre>*/public void setPrettyPrint(boolean prettyPrint) {this.prettyPrint = prettyPrint;configurePrettyPrint();}private void configurePrettyPrint() {if (this.prettyPrint != null) {this.objectMapper.configure(SerializationFeature.INDENT_OUTPUT, this.prettyPrint);}}@Overrideprotected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage)throws IOException, HttpMessageNotReadableException {JavaType javaType = getJavaType(clazz, null);return readJavaType(javaType, inputMessage);}@Overridepublic Object read(Type type, @Nullable Class<?> contextClass, HttpInputMessage inputMessage)throws IOException, HttpMessageNotReadableException {JavaType javaType = getJavaType(type, contextClass);return readJavaType(javaType, inputMessage);}private Object readJavaType(JavaType javaType, HttpInputMessage inputMessage) throws IOException {try {if (inputMessage instanceof MappingJacksonInputMessage) {Class<?> deserializationView = ((MappingJacksonInputMessage) inputMessage).getDeserializationView();if (deserializationView != null) {return this.objectMapper.readerWithView(deserializationView).forType(javaType).readValue(inputMessage.getBody());}}return this.objectMapper.readValue(inputMessage.getBody(), javaType);}catch (InvalidDefinitionException ex) {throw new HttpMessageConversionException("Type definition error: " + ex.getType(), ex);}catch (JsonProcessingException ex) {throw new HttpMessageNotReadableException("JSON parse error: " + ex.getOriginalMessage(), ex, inputMessage);}}@Overrideprotected void writeInternal(Object object, @Nullable Type type, HttpOutputMessage outputMessage)throws IOException, HttpMessageNotWritableException {MediaType contentType = outputMessage.getHeaders().getContentType();JsonEncoding encoding = getJsonEncoding(contentType);JsonGenerator generator = this.objectMapper.getFactory().createGenerator(outputMessage.getBody(), encoding);try {writePrefix(generator, object);Object value = object;Class<?> serializationView = null;FilterProvider filters = null;JavaType javaType = null;if (object instanceof MappingJacksonValue) {MappingJacksonValue container = (MappingJacksonValue) object;value = container.getValue();serializationView = container.getSerializationView();filters = container.getFilters();}if (type != null && TypeUtils.isAssignable(type, value.getClass())) {javaType = getJavaType(type, null);}ObjectWriter objectWriter = (serializationView != null ?this.objectMapper.writerWithView(serializationView) : this.objectMapper.writer());if (filters != null) {objectWriter = objectWriter.with(filters);}if (javaType != null && javaType.isContainerType()) {objectWriter = objectWriter.forType(javaType);}SerializationConfig config = objectWriter.getConfig();if (contentType != null && contentType.isCompatibleWith(MediaType.TEXT_EVENT_STREAM) &&config.isEnabled(SerializationFeature.INDENT_OUTPUT)) {objectWriter = objectWriter.with(this.ssePrettyPrinter);}objectWriter.writeValue(generator, value);writeSuffix(generator, object);generator.flush();}catch (InvalidDefinitionException ex) {throw new HttpMessageConversionException("Type definition error: " + ex.getType(), ex);}catch (JsonProcessingException ex) {throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getOriginalMessage(), ex);}}/*** Return the Jackson {@link JavaType} for the specified type and context class.* @param type the generic type to return the Jackson JavaType for* @param contextClass a context class for the target type, for example a class* in which the target type appears in a method signature (can be {@code null})* @return the Jackson JavaType*/protected JavaType getJavaType(Type type, @Nullable Class<?> contextClass) {TypeFactory typeFactory = this.objectMapper.getTypeFactory();return typeFactory.constructType(GenericTypeResolver.resolveType(type, contextClass));}
}

总结一下:支持的ObjectMapper特性:

  1. 29行:根据配置开启格式化序列化
  2. 128行:根据Class获取JavaType,可以支持@JsonTypeInfo
  3. 54行:反序列化(读)支持运行时@JsonView
  4. 84行:序列化(写)支持运行时@JsonView
  5. 86行:序列化(写)支持运行时@JsonFilter

@JsonView@JsonFilter是个比较好玩的特性,可以动态忽略字段的序列化/反序列化。比较典型的场景是POSTPUT对象通常是一样的,但是POST不需要传idPUT必须要传id,这时候通常没必要单独为POST建一个对象,那么可以通过@JsonView创建一个POST读视图忽略读取id

SpringWebJsonViewResponseBodyAdvice中默认提供了对@JsonView的支持,但是开启@JsonFilter需要一些额外的工作,这里不展开了,有兴趣的同学可以自己尝试玩一玩比如结合@JsonIngoreProperties动态忽略响应字段~

一文读懂Json序列化与反序列化相关推荐

  1. java序列化_夯实Java基础系列22:一文读懂Java序列化和反序列化

    本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial 喜欢的话麻烦点下 ...

  2. 夯实Java基础系列22:一文读懂Java序列化和反序列化

    本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial 喜欢的话麻烦点下 ...

  3. 一文读懂Java中File类、字节流、字符流、转换流

    一文读懂Java中File类.字节流.字符流.转换流 第一章 递归:File类: 1.1:概述 java.io.File 类是文件和目录路径名的抽象表示,主要用于文件和目录的创建.查找和删除等操作. ...

  4. 一文搞懂JSON.stringify和JSON.parse(五)JSON.parse使用说明

    一 JSON.parse特性 JSON.parse()就是将JSON字符串解析成字符串描述的JavaScript值或对象(将JSON数据解析为js原生值),例如: JSON.parse('{}'); ...

  5. 一文读懂:什么是区块链

    今天写一个能够一文读懂区块链的文章,以后谁再问我区块链是什么东西,我就把这篇文章发给他. -------- 个人技术公众号:解决方案工程师 欢迎同领域的朋友关注.相互交流. -------- 区块链技 ...

  6. php和js序列化,PHP中serialize和json序列化与反序列化的区别

    在PHP中,serialize和json两种方式对一个对象或数组进行序列化或反序列化有什么区别呢? 假设一个对象和一个数组: PHP 1 2 3 4$web=newstdClass; $web-> ...

  7. 一文读懂元数据管理!

    原文:一文读懂元数据管理! - 知乎 数字化时代,企业需要知道他们拥有什么数据,数据在哪里.由谁负责,数据中的值意味着什么,数据的生命周期是什么,哪些数据安全性和隐私性需要保护,以及谁使用了数据,用于 ...

  8. 一文读懂JS继承相关知识点

    一文读懂JS继承相关知识点 Javascript 面向对象编程(一):封装 一. 生成实例对象的原始模式 二. 原始模式的改进 三. 构造函数模式 四.构造函数模式的问题 五. Prototype模式 ...

  9. java rest 序列化_一文看懂Java序列化

    一文看懂Java序列化 简介 首先我们看一下wiki上面对于序列化的解释. 序列化(serialization)在计算机科学的数据处理中,是指将数据结构或对象状态转换成可取用格式(例如存成文件,存于缓 ...

最新文章

  1. celery的初次使用
  2. 【数学与算法】贝塞尔曲线
  3. C++的坑真的多吗?
  4. linux下的环境变量/etc/profile、/etc/bashrc、~/.bash_profile、~/.bashrc文件
  5. ubuntu100%快速安装搜狗输入法
  6. sql设置id自动增长_JDBC进阶(一):批处理与获取自增长列的值
  7. (转)MTK softkey流程 必看
  8. 《高性能MySQL》之选择优化的数据类型
  9. cordic ip核 vivado_Xilinx Vivado Cordic 核的使用
  10. 转:我的创业梦想,一个关于照片的网站
  11. 骑士CMSgetshell复现
  12. 解决Attribute 'transaction-manager' is not allowed to appear
  13. WooCommerce REST API的初学者指南
  14. win10最简单定时关机命令
  15. 辅助 Excel 的数据计算 add-ins
  16. 手柄xinput模式_玩家新宠,谷粒金刚PRO游戏手柄不全面体验
  17. 【Nginx】Nginx配置实例-反向代理
  18. 手把手教你设置路由器端口映射
  19. 本地提交spark_Spark 任务提交
  20. 54亿美元!谷歌收购Mandiant,推动网络安全行业创新

热门文章

  1. tess4J 安装使用
  2. 菜鸟c语言编程,C语言菜鸟基础教程之Hello World
  3. 游戏中制作酷炫拖尾残影效果实战
  4. 跟着神雕侠侣游九寨沟
  5. 四款经典3.7v锂电池充电电路图详解 - 全文锂电池的内部结构所致,必须保留一部分锂离子在负极,锂电池不能过放电
  6. 情人节程序员用HTML网页表白【七夕烟花】 HTML5七夕情人节表白网页源码 HTML+CSS+JavaScript
  7. 服务器端过滤参数中的回车换行
  8. OC中NSDictionary常用方法总结
  9. Threejs 学习记录:初识 threejs
  10. 从零开始vim搭建Java开发环境之coc.nvim 篇