SpringMVC Controller层优雅的处理指定类型返回格式 之 使用Jackson指定注解处理

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;
	}

}

2.@JsonFormat 处理时间

@JsonFormat(pattern = "yyyy-MM-dd HH-mm-ss")
 private Date expiredDate;

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章