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

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

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

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反序列化使用的是哪种方式呢?是第一种啦!所以不要在构造器中埋坑哦~

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 {
     @Data
     static 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));
 
         @Override
         public 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(
                 // 禁用写 nanos
                 SerializationFeature.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_instant
         String 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);
     }
 
     @Data
     static 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请求处理流程

tomcat DispatcherServlet HanlderMapping HandlerAdapter HttpRequest doDispatch() HandlerMapping.getHandler() RequestMappingHandlerMapping.getHandlerInternal() HandlerExecutionChain getHandlerAdaptor HandlerExecutionChain.applyPreHandle() HandlerAdapter.handle() RequestMappingHandlerAdapter.handleInternal() 在这里解析请求、处理响应:(反)序列化Java对象 ModelAndView HandlerExecutionChain.applyPostHandle() ModelAndView.getView().render() HttpResponse tomcat DispatcherServlet HanlderMapping HandlerAdapter SpringMvc处理流程

一般即在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);
		}
	}

	@Override
	protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage)
			throws IOException, HttpMessageNotReadableException {

		JavaType javaType = getJavaType(clazz, null);
		return readJavaType(javaType, inputMessage);
	}

	@Override
	public 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);
		}
	}

	@Override
	protected 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动态忽略响应字段~

  • 14
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
"Faster"是一个英文单词,它的意思是更快的意思。在不同的背景下,它可以有不同的解释和应用。 在日常生活中,"faster"通常表示一个行动或者动作的速度比以往更快。例如,当我们走路、跑步、开车或者骑自行车时,我们可以尝试跑得更快或者骑得更快。这个词还可以用来形容物体的移动速度,比如火车、飞机或者摩托车的速度。此外,"faster"也可以用来描述人们的反应速度、思维速度或者学习速度等。 此外,在科技和互联网领域,"faster"也具有很重要的含义。随着科技的不断进步,人们追求更快的处理速度和传输速度。例如,我们常常会听到"更快的互联网连接"、"更快的计算机处理速度"、"更快的数据传输速度"等等。这些技术进步使得信息的获取和交流变得更加高效和便捷。 然而,在追求速度的同时,我们也要权衡速度和其他因素之间的平衡。有时候,过于追求速度可能会带来其他的负面影响,比如对安全性和质量的妥协。所以在实际应用中,需要综合考虑各种因素来确定速度的优先级。 总之,"faster"是一个多功能的词,可以用来描述日常生活中的行动速度,物体的移动速度,人们的思维或者学习速度,以及科技领域的处理速度和传输速度等等。同时,我们也要在追求速度的过程中,平衡各种因素并合理使用这个词。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

theskyzero

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值