Controller层优雅的处理指定类型返回格式 之 使用Jackson指定注解处理
一、可以用哪些注解?
在我们日常开发中可能会有对指定类型的参数统一处理返回,比如金额、日期等等
下面来介绍几种简单的使用Json注解的方式来处理这中统一返回的类型
1. @JsonDeserializ 注解
Controller层使用@RequestBody时 以set的方式去set指定格式值
2. @JsonSerialize注解
Controller层使用 @ResponseBody 时以get的方式去响应给前端指定格式的值
3.@JsonFormat注解
该注解是针对Date类型直接转化为我们想要的模式,比如@JsonFormat(pattern = "yyyy-MM-dd HH-mm-ss")。当然也可以使用
1和2两个注解去设置
这三个注解可以用于属性或者方法上(最好是属性上)但是需要注意如果在方法上使用则@JsonDeserializ注解只能用在set方法上面@JsonSerialize注解只能用在get方法上面。
二、具体使用
1. @JsonSerialize注解与@JsonDeserializ 注解
源码
package com.fasterxml.jackson.databind.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.util.Converter;
/**
通过附加用于配置序列化方面的注解“getter”方法或字段,或值类。
当注解值类时,配置用于实例,但可以被更特定的注解覆盖(附加到方法或字段上的)。
举例说明如下:
@JsonSerialize(using= MySerializer.class,
as = MySubClass.class,type= JsonSerialize.Typing.STATIC
)
(这是多余的,因为有些属性会阻塞其他属性:
具体来说,‘using’优先于‘as’,后者优先
在“类型”设置)
*/
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.TYPE, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@com.fasterxml.jackson.annotation.JacksonAnnotation
public @interface JsonSerialize
{
// 用于显式指定反序列化器的注解
/**
要用于的序列化器类序列化相关联的值。根据注解的内容,值是带注解的类的实例(全局使用任何需要类序列化器的地方);
或者只用于通过getter方法序列化属性访问。
*/
@SuppressWarnings("rawtypes") // to work around JDK8 bug wrt Class-valued annotation properties
public Class<? extends JsonSerializer> using() default JsonSerializer.None.class;
/**
用于序列化内容(元素的序列化器类属性的集合/数组的值)。只能用于属性(方法、字段、构造函数),而不是值类本身(因为它们通常是泛型的)
*/
@SuppressWarnings("rawtypes") // to work around JDK8 bug wrt Class-valued annotation properties
public Class<? extends JsonSerializer> contentUsing()
default JsonSerializer.None.class;
/**
用于序列化映射键的序列化器类带注解的属性。只能用于属性(方法、字段、构造函数),而不是类本身的值。
*/
@SuppressWarnings("rawtypes") // to work around JDK8 bug wrt Class-valued annotation properties
public Class<? extends JsonSerializer> keyUsing()
default JsonSerializer.None.class;
/**
用于序列化该属性的空值的是带注解的,而不是默认空序列化器。注意,当注解类型(类)具有目前没有效果(以后可能会改善)。
@since 2.3
*/
@SuppressWarnings("rawtypes") // to work around JDK8 bug wrt Class-valued annotation properties
public Class<? extends JsonSerializer> nullsUsing()
default JsonSerializer.None.class;
// 用于类型处理的注解,显式声明
// 用于选择反序列化器的类型,如果不是显式的
// 指定
/**
超类型(声明类型的,它本身是运行时类型的超类型)在定位要使用的序列化程序时用作类型。
伪类型{@link Void}可用于指示声明的类型类型按原样使用(即该注解属性没有设置);这是因为注解属性不允许为空。
注意:如果也使用{@link #using},则它具有优先(因为它直接指定了,而这仅用于定位序列化器)这个annotation属性的值将被忽略。
*/
public Class<?> as() default Void.class;
/**
用于序列化{@link java.util.Map},而不是以其他方式声明的类型。必须是声明类型的超类型;否则可能会出现例外抛出的序列化器。
*/
public Class<?> keyAs() default Void.class;
/**
Concrete type to serialize content value (elements
of a Collection/array, values of Maps) as,
instead of type otherwise declared.
Must be a supertype of declared type; otherwise an exception may be
thrown by serializer.
用于序列化内容值(元素)的具体类型的集合/数组,映射的值)为,而不是以其他方式声明的类型。必须是声明类型的超类型;否则可能会出现例外抛出的序列化器。
*/
public Class<?> contentAs() default Void.class;
/**
Whether type detection used is dynamic or static: that is,
whether actual runtime type is used (dynamic), or just the
declared type (static).
Note that Jackson 2.3 changed default to <code>DEFAULT_TYPING</code>,
which is roughly same as saying "whatever".
This is important as it allows avoiding accidental overrides
at property level.
使用的类型检测是动态的还是静态的:是否使用实际运行时类型(动态),或者仅使用声明类型(静态)。
注意Jackson 2.3将默认值更改为DEFAULT_TYPING,这大致相当于说“任何”。这很重要,因为它允许避免意外覆盖在属性级别。
*/
public Typing typing() default Typing.DEFAULT_TYPING;
// 指定中间转换器的注解(2.2+)
/**
要使用哪个helper对象将类型转换为内容Jackson知道如何序列化;要么是因为基类型不能轻易序列化,或者只是为了改变序列化。
*
* @since 2.2
*/
@SuppressWarnings("rawtypes") // to work around JDK8 bug wrt Class-valued annotation properties
public Class<? extends Converter> converter() default Converter.None.class;
/**
Similar to {@link #converter}, but used for values of structures types
(List, arrays, Maps).
Note that this property does NOT have effect when used as Class annotation;
it can only be used as property annotation: this because association between
container and value types is loose and as such converters seldom make sense
for such usage.
类似于{@link #converter},但用于结构类型的值(List, arrays, Maps)。
注意,当用作类注解时,此属性没有作用;这样的用法它只能用作属性注解:这是因为关联容器和值类型是松散的,因此这种转换器很少有意义
*
* @since 2.2
*/
@SuppressWarnings("rawtypes") // to work around JDK8 bug wrt Class-valued annotation properties
public Class<? extends Converter> contentConverter() default Converter.None.class;
// 包含标准的注解
/**
注解Bean的哪些属性被包含在序列化中(对其他类型没有影响例如枚举、原语或集合)。
选项是"all" "properties that have value other than null"以及“具有非默认值的属性”(即默认值为使用默认no-arg构造的Bean设置属性构造函数,通常是零)。
这个属性已经被专用的@deprecated替换。注意Jackson 2.3将default更改为DEFAULT_INCLUSION,这大致相当于说“随便”。这很重要,因为它允许使用分层的默认值。
*/
@Deprecated
public Inclusion include() default Inclusion.DEFAULT_INCLUSION;
/*
/**********************************************************
/* 枚举值需要
/**********************************************************
*/
/**
Enumeration used with {@link JsonSerialize#include} property
to define which properties
of Java Beans are to be included in serialization
枚举与 @Deprecated 属性一起使用定义哪些属性的Java bean将包含在序列化中
*/
@Deprecated // since 2.0, marked deprecated in 2.6
public enum Inclusion
{
/**
值,该值指示始终包含属性,独立的价值
*/
ALWAYS,
/**
值,该值指示仅具有非null属性要包含值。
*/
NON_NULL,
/**
值,该值指示只有具有值的属性不同于默认设置(即它们拥有的值当Bean用它的无参数构造函数构造时)包括在内。值通常是没有用的
{@link java.util。Map},因为它们没有默认值;如果使用,工作方式与{@link #ALWAYS}相同。
*/
NON_DEFAULT,
/**
值,该值指示只有具有值的属性为空或被认为为空的值为不包括在内。空被定义为以下类型:
List和Map方法isEmpty()被调用;
对于Java数组,空数组是长度为0的数组
用于Java String, length() 返回值0表示空字符串
对于其他类型,将包含非空值。
*/
NON_EMPTY,
/**
用于指示的伪值“使用上级默认使用的任何内容”。
*/
DEFAULT_INCLUSION
;
}
/**
与Typing属性一起使用的枚举,用于定义类型检测是基于动态运行时类型(动态)还是声明的类型(静态)。
*/
public enum Typing
{
/**
值,该值指示实际的动态运行时类型为被使用。
*/
DYNAMIC,
/**
值,该值指示将使用静态声明类型
*/
STATIC,
/**
用于指示的伪值“使用上级默认使用的任何内容”。
*/
DEFAULT_TYPING
;
}
}
我们可以看里面有很多属性
其中我们先看第一个using
在源码中显示该注解属性的值必须是一个继承了JsonSerializer的class
public Class<? extends JsonSerializer> using() default JsonSerializer.None.class;
走进JsonSerializer 源码
/**
可调用该方法来要求实现序列化此序列化程序句柄类型的值。
*
* @param value Value to serialize; can <b>not</b> be null.
* @param gen Generator used to output resulting Json content
* @param serializers Provider that can be used to get serializers for
* serializing Objects value contains, if any.
*/
public abstract void serialize(T value, JsonGenerator gen, SerializerProvider serializers)
throws IOException, JsonProcessingException;
其中只有一个待实现的方法 serialize 方法并且他还有三个参数
1.value 也就是我们待处理的值
2.gen 用于输出结果Json内容的gen生成器
有这两个呢我们就已经可以简单知道需要怎么操作了,下面我以统一处理BigDecimal为例
我们日常处理金额会有一个金额乘以100 然后返回给前端,前端要是使用则除以一百即可
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import java.math.BigDecimal;
/**
* 浮点型乘100返回整型统一json注解处理
*
* 直接在字段上面添加一下注解
*
* 例如:
* @JsonSerialize(using = BigDecimalJsonSerialize.class)
* private BigDecimal productPrice;
* @Author 王福强
* @Since 2019年06月03日 15:15
* @Email [email protected]
*/
public class BigDecimalJsonSerialize extends JsonSerializer<BigDecimal> {
private BigDecimal oneHundred = new BigDecimal(100);
@Override
public void serialize(BigDecimal value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
if(value != null) {
//最后输出的值 value.multiply() 是乘以某个指定值,上面定义了100 所以这里就是*100
gen.writeNumber(value.multiply(oneHundred));
}
}
}
然后就可以看结果了
不使用注解的情况
使用注解后
@JsonDeserializ 与@ JsonSerialize 其实也差不多 需要实现该方法
public abstract T deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException;
将前端传过来的*100 的整数除以一百转换为浮点数
public class BigDecimalJsonDeserialize extends JsonDeserializer<BigDecimal> {
private BigDecimal oneHundred = new BigDecimal(100);
@Override
public BigDecimaldeserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
Number numberValue = null;
BigDecimal divide = null;
try {
numberValue = jp.getNumberValue();
BigDecimal bigDecimal = new BigDecimal(numberValue.intValue());
divide = bigDecimal.divide(oneHundred, 2, BigDecimal.ROUND_HALF_UP);
} catch (IOException e) {
e.printStackTrace();
}
return divide;
}
}
[email protected] 处理时间
@JsonFormat(pattern = "yyyy-MM-dd HH-mm-ss")
private Date expiredDate;