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;