Jackson使用进阶

news2024/11/29 4:33:27

实现

注解

属性命名

@JsonProperty

定义属性序列化时的名称。

@JacksonAnnotation
public @interface JsonProperty
{
    public final static String USE_DEFAULT_NAME = "";
    public final static int INDEX_UNKNOWN = -1;
	//指定属性的名称。
    String value() default USE_DEFAULT_NAME;
    boolean required() default false;
    //如果数据格式(JSON 除外)是基于索引的,则要使用的物理索引
    int index() default INDEX_UNKNOWN;
    //定义为元数据的文本默认值。注意:核心数据绑定不会使用此值;它目前仅向扩展模块公开。
    String defaultValue() default "";
    //用来控制是否 能被【序列化】或者【反序列化】,默认是不受控制的。
    Access access() default Access.AUTO;
    
    public enum Access
    {
        AUTO,
        //仅做序列化操作
        READ_ONLY,
        //仅做反序列化操作
        WRITE_ONLY,
        //都可以
        READ_WRITE
        ;
    }
}

属性包含

@JsonAutoDetect

注解在类上,用于覆盖属性的定义。

public @interface JsonAutoDetect
{
    /** 可视化
     */
    public enum Visibility {
        /** 所有的访问限制符都可以。从private 到 public */
        ANY,
        /** 非 Private 是 自动侦测的。 */
        NON_PRIVATE,
        /** protected 和 public 是 自动侦测的  */
        PROTECTED_AND_PUBLIC,
        /** 仅 public 是 自动侦测的  */
        PUBLIC_ONLY,
        /** 所有都不自动侦测,用于关闭自动侦测 */
        NONE,
        
        /** 由 context 决定 */
        DEFAULT;
 
    }
    
    /**
     * getter 最小 可视化需求
     */
    Visibility getterVisibility() default Visibility.DEFAULT;
    Visibility isGetterVisibility() default Visibility.DEFAULT;
    Visibility setterVisibility() default Visibility.DEFAULT;
    Visibility creatorVisibility() default Visibility.DEFAULT;
    Visibility fieldVisibility() default Visibility.DEFAULT;

}

@JsonIgnore

注解用于属性或者方法上,用来完全忽略被注释的字段和方法对应的属性。

放在getter上也会禁用setter,除非在setter上放置 @JsonProperty

@JsonIgnoreProperties

注解在类上,用于忽略指定的属性。

@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE,
    ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonIgnoreProperties
{
    /**
     * Names of properties to ignore.
     */
    public String[] value() default { };
 	//反序列化时,忽略 未知的json 属性。
    public boolean ignoreUnknown() default false;
 
    public boolean allowGetters() default false;
    public boolean allowSetters() default false;
}

@JsonIgnoreType

注解在类上,用于忽略指定 类型 的属性。

@JsonInclude

用于排除值为empty/null/default的属性。

@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD,
    ElementType.TYPE, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonInclude
{
    //包含策略
    public Include value() default Include.ALWAYS;

    /** Map,AtomicReference 等entity 的 规则      */
    public Include content() default Include.ALWAYS;

    /**  value 属性设置为CUSTOM时,配合 过滤*/
    public Class<?> valueFilter() default Void.class;

     /**  content 属性设置为CUSTOM时,配合 过滤*/
    public Class<?> contentFilter() default Void.class;
    
 
    public enum Include
    {
        /** 属性一直被包含,无关具体属性值 */
        ALWAYS,

        /** 非空值才会被包含 **/
        NON_NULL,

        /**Optional或AtomicReference引用的实例为null时,也可使该字段不做序列化,同时可以排除值为null的字段*/
        NON_ABSENT,

        /** 排除字段值为null、空字符串、空集合、空数组、Optional类型引用为空,AtomicReference类型引用为空 */
        NON_EMPTY,

        /** 排除 是指定类型的默认值的 属性 */
        NON_DEFAULT,

        /**配合valueFilter属性一起使用,在序列化的时候会执行CustomFilter中的的equals方法,该方法的入参就是字段的值,如果equals方法返回true,字段就不会被序列化,如果equals方法返回false时字段才会被序列化*/
        CUSTOM,
        
        /** 使用默认设置
         */
        USE_DEFAULTS
        
        ;
    }

@JsonFilter

属性文档与元数据

@JsonPropertyDescription

用于生成字段的描述信息。

序列化与反序列化细节

@JsonFormat

用于格式化日期/时间格式的数据。

@Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER,
    ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonFormat
{
 
    public final static String DEFAULT_LOCALE = "##default";
    public final static String DEFAULT_TIMEZONE = "##default";
 	//指定格式化字符串,例如:YYYY-MM-DD
    public String pattern() default "";
 	// 序列化后的类型
    public Shape shape() default Shape.ANY;
   //默认locale
    public String locale() default DEFAULT_LOCALE;
   //默认时区
    public String timezone() default DEFAULT_TIMEZONE;
   //
    public OptBoolean lenient() default OptBoolean.DEFAULT;
 	//
    public JsonFormat.Feature[] with() default { };
 	//
    public JsonFormat.Feature[] without() default { };
    //JSON 的数据类型
    public enum Shape
    {
        ANY,
        NATURAL,
        SCALAR,
        ARRAY,
        OBJECT,
        NUMBER,
        NUMBER_FLOAT,
        NUMBER_INT,
        STRING,
        BOOLEAN,
        BINARY
        ;
}

@JsonUnwrapped

将对象扁平,即将属性实体的属性作为本对象的属性。

@Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonUnwrapped
{
    boolean enabled() default true;

    /** 输出属性名的前缀 */
    String prefix() default "";

     /** 输出属性名的后缀 */
    String suffix() default "";
}
class Employee {
   public int empId = 110;
   public String empName = "Raja Ramesh";
   @JsonUnwrapped
   public Address address = new Address();
   //地址类别 
   public static class Address {
      public String doorNumber = "1118";
      public String street = "madhapur";
      public String pinCode = "500081";
      public String city = "Hyderabad";
   }
}

结果:

{
   "empId" : 110,
   "empName" : "Raja Ramesh",
   "doorNumber" : "1118",
   "street" : "madhapur",
   "pinCode" : "500081",
   "city" : "Hyderabad"
}

@JsonView

可以用于过滤属性。适用于在不同场景下,同一个字段是否输出。比如密码,在某些情况下返回,某些情况下不返回。

public class JsonViewTest {

   //定义了2个场景
    public interface SimpleView{};
    public interface DetailView extends SimpleView{} ;


    private static class User{
        static ObjectMapper objectMapper = new ObjectMapper();
        
        private String age;
        @JsonView(SimpleView.class)
        private String userName;
        @JsonView(DetailView.class)
        private String password;
        private String sex;


        public static String getSimpleView()throws Exception{
            User user = getUser();
            return objectMapper.writerWithView(SimpleView.class).writeValueAsString(user);
        }

        public static String getDetailView()throws Exception{
            User user = getUser();
            return objectMapper.writerWithView(DetailView.class).writeValueAsString(user);
        }

        private static User getUser() {
            User user = new User();
            user.setAge("11111");
            user.setPassword("pwd");
            user.setSex("man");
            user.setUserName("name");
            return user;
        }

    }
}

User.getSimpleView() 结果:

{"age":"11111","userName":"name","sex":"man"}

(User.getDetailView() 结果:

{"age":"11111","userName":"name","password":"pwd","sex":"man"}

没有加 @JsonView 的属性,则忽略场景。

Spring MVC 的使用
@RestController
public class SimpleRestController {
    @Autowired
    SimpleService simpleService;
    @RequestMapping(value = "/user/simple", method = RequestMethod.GET)
    @JsonView(SimpleView.class)
    public User getUserWithSimpleData() {
        return User.getUser();
    }
    @RequestMapping(value = "/user/detail", method = RequestMethod.GET)
    @JsonView(DetailView.class)
    public User getUserWithDetailData() {
        return User.getUser();
    }
}

在方法上加@JsonView,则会在Spring MVC调用 objectMapper时,自动应用。

反序列化细节

@JsonAlias

指定属性的一个或多个别名,只用于反序列化。即属性可以从多个JSON KEY中获取到值。可指定多个别名。

@JsonMerge

通过JSON字符串对已存在的对象实例进行更新。仅当属性值为null时,才会更新。

@JacksonInject

指定某个字段从注入赋值,而不是从Json。如果一个属性用 @JacksonInject 注解,并且我们使用 ObjectMapper 注入了值,那么默认情况下如果没有对应的 JSON 字段,注入的值将是属性的值,如果有对应的 JSON 字段,则属性值将被相应的覆盖为JSON 字段值。

@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JacksonInject
{
    /** 注入值的逻辑id,如果没有指定,则根据属性类型注入 */
    public String value() default "";

    /** 决定 匹配的输入值是否覆盖注解的属性。如果该值为 false,则输入值将被忽略。如果值为 true,则输入值将覆盖注入值。 */
    public OptBoolean useInput() default OptBoolean.DEFAULT;
}

使用 ObjectMapper 类的 setInjectableValues 方法注入值。

指定value属性示例:

@JacksonInject("wnameInput")
private String name;
	
@JacksonInject("bookInput")
private Book book;
 
injectableValues.addValue("wnameInput","Mahesh");
injectableValues.addValue("bookInput", new Book("Hibernate Tutorial", "Hibernate"));
mapper.setInjectableValues(injectableValues);

不指定value属性示例:

@JacksonInject
private String name;
	
@JacksonInject
private Book book;

injectableValues.addValue(String.class,"Mahesh");
injectableValues.addValue(Book.class, new Book("Hibernate Tutorial", "Hibernate"));
		 
mapper.setInjectableValues(injectableValues);

使用:


// InjectableValues
InjectableValues.Std injectableValues = new InjectableValues.Std();
injectableValues.addValue(String.class,"Default Value");
injectableValues.addValue("bookInput", new Book("Hibernate Tutorial", "Hibernate"));

ObjectMapper mapper = new ObjectMapper();
mapper.setInjectableValues(injectableValues);
//json 转对象
Writer writer = mapper.readValue(jsonData, Writer.class);
System.out.println(writer.getId()+", "+ writer.getName());
Book book = writer.getBook();
System.out.println(book.getName()+", "+ book.getCategory());

@JsonAnySetter

标记在setter方法上或属性上,此方法参数为键值对,反序列化时所有不包含在对象中的未知属性都会调用此setter方法设置。

 
class Book {
    private String name;
    //除了name属性,其他的属性都放置到 properties中。
    @JsonAnySetter
    private Map<String, String> properties = new HashMap<>();
 
    public Map<String, String> getProperties() {
        return properties;
    }
 
    public void setProperties(Map<String, String> properties) {
        this.properties = properties;
    }
    /**
    或者使用此方法
    @JsonAnySetter
    public void setAdditionalProperty(String name, Object value) {
        this.additionalProperties.put(name, value);
    }
    */
}
 

@JsonCreator

为Java对象创建指定一个构造方法,在无法使用@JsonSetter注解的情况下很有用。例如,不可变对象没有任何设置方法,因此它们需要将其初始值注入到构造函数中。即通过构造函数,设置对象属性。

public class PersonImmutable { 
    private long id   = 0; 
    private String name = null; 
    //指定通过这个进行属性设置。
    @JsonCreator 
    public PersonImmutable(
     //每个属性对应哪个参数。
     @JsonProperty("id") long id,
     @JsonProperty("name") String name  ) 
     { 
        this.id = id; 
        this.name = name;
     } 

} 

@JsonSetter

当将JSON读入对象时,应将此setter方法的名称与JSON数据中的属性名称匹配。如果Java类内部使用的属性名称与JSON文件中使用的属性名称不同,这个注解就很有用了。

class Book {
    private String name;
    //把JSON中的 bookName 属性值 读取 为 这个 方法。则设置了name的值。
    @JsonSetter("bookName")
    public void setName(String name){
    	this.name = name;
    }
}    

@JsonEnumDefaultValue

可以通过 @JsonEnumDefaultValue 注解为未知的枚举类型赋一个默认值来兜底,但要记得在 ObjectMapper 中手动开启该功能。

private enum Sex {
    MAN,
    WOMAN,
    @JsonEnumDefaultValue
    UNKNOWN
}

序列化细节

@JsonAnyGetter

可以将Map用作要序列化为JSON的属性。

@JsonGetter

指定某个方法的返回值,用做JSON属性的值。

@JsonPropertyOrder

指定序列化时属性的顺序。

@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE,
    ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonPropertyOrder
{
    /**
     * 在此处指定属性的顺序,属性的数组。
     */
    public String[] value() default { };

    /** 按字母顺序*/
    public boolean alphabetic() default false;
}

@JsonRawValue

会将字符串形式的 JSON 也尝试序列化为对象。即如果属性是JSON字符串,则会序列化为JSON对象,而不再是个String属性。

@JsonValue

不要由jackson序列化,而是调用 加此注解的方法的返回值作为JSON 串。比如 @JsonValue toJson() 方法。

@JsonRootName

指定JSON对象的根名称。即把输出的JSON再包装一层,指定给设置的属性。

@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@com.fasterxml.jackson.annotation.JacksonAnnotation
public @interface JsonRootName
{
    public String value();
 
    public String namespace() default "";
 
}
 @JsonRootName("user")
 class User{
	private String age;
	private String userName;
	private String sex;
}
//输出:设置了根属性。
{"user":
    {"age":"11111","userName":"name","sex":"man"}
}	

@JsonAppend

在序列化时额外添加指定的属性。

@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@com.fasterxml.jackson.annotation.JacksonAnnotation
public @interface JsonAppend
{
    /** 属性支持的属性 */
    public Attr[] attrs() default { };

    /**
     * 通用虚拟属性。.
     */
    public Prop[] props() default { };

    /** 虚拟属性附加在常规属性前面还是右面,默认false(后面) */
    public boolean prepend() default false;

    /** 单个属性定义。 */
    public @interface Attr
    {
        /** 属性名称,也用于扩展属性,直到被propName 覆盖。  */
        public String value();

        /** 属性名称,如果未定义,则使用value属性。 */
        public String propName() default "";

        /** xml的属性namespace。 */
        public String propNamespace() default "";

        public JsonInclude.Include include() default JsonInclude.Include.NON_NULL;

        public boolean required() default false;
    }
    
    /**
     * Definition of a single general virtual property.
     */
    public @interface Prop
    {
        /* 要实例化的属性的实际实现类(VirtualBeanPropertyWriter子类)  */
        public Class<? extends VirtualBeanPropertyWriter> value();

        /**可能用于序列化的属性的名称(尽管实现可以选择不使用此信息)。*/
        public String name() default "";

        /** xml namespace */
        public String namespace() default "";

        /** 何时包含属性值。 NON_NULL 指示 仅当属性值不为null时才写。其他值,与其他属性一样,实际的属性实现可以选择也可以不选择使用此包含信息。*/
        public JsonInclude.Include include() default JsonInclude.Include.NON_NULL;

        /**
         * Metadata about property, similar to
         * {@link com.fasterxml.jackson.annotation.JsonProperty#required()}.
         */
        public boolean required() default false;

        /** 属性的名义类型。作为相关虚拟对象的类型信息传递,实现可以(也可以不)使用它来选择要使用的序列化器。  */
        public Class<?> type() default Object.class;
    }
}

多态类型处理

多态类型处理主要是解决派生类的序列化与反序列化问题。比如Animal 的子类 Dog,Cat 等。

@JsonTypeId

标识注解,用在字段、getter、setter、构造函数参数上,用以指示是个多态类型。

@JsonTypeInfo

@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE,
    ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonTypeInfo
{    
    public enum Id {
        NONE(null),
        CLASS("@class"),
        MINIMAL_CLASS("@c"),
        NAME("@type"),
        CUSTOM(null)
        ;
        // 默认属性名称。
        private final String _defaultPropertyName;

        private Id(String defProp) {
            _defaultPropertyName = defProp;
        }

        public String getDefaultPropertyName() { return _defaultPropertyName; }
    }
 
    public enum As {
        PROPERTY,
        WRAPPER_OBJECT,
        WRAPPER_ARRAY,
        EXTERNAL_PROPERTY,
        EXISTING_PROPERTY
        ;
    }
    
    
    /** 这个字段是用来指定根据哪种类型的元数据来进行序列化和反序列化  */
    public Id use();    
    
    /** 这个字段是用来指定元信息是如何被包含进去的   */
    public As include() default As.PROPERTY;

    /** 只用当`use`为`JsonTypeInfo.Id.CUSTOM`,或者`include`为`JsonTypeInfo.As.PROPERTY`时才会配置这个值。这个就可以用来表示具体的依据字段。 */
    public String property() default "";
   //如果类型识别码不存在或者无效,可以使用该属性来制定反序列化时使用的默认类型
    public Class<?> defaultImpl() default JsonTypeInfo.class;
   /**定义了类型标识符的值是否会通过JSON流成为反序列化器的一部分,默认为fale,也就是说,jackson会从JSON内容中处理和删除类型标识符再传递给JsonDeserializer。
   */
    public boolean visible() default false;
}   

use解释

用于指定子类用什么来进行区分。

  • NONE:不包括显式的类型元数据,类型完全是使用上下文信息完成的,可能还添加了其他注解。
  • CLASS:类型的全限定名作为类的标识符。
  • MINIMAL_CLASS:若基类和子类在同一包类,使用相对于基类的相对全限名作为识别码。例如基类com.foobar.Base的具体类 com.foo.Impl 的id为.Impl,具体类com.foo.impl.Impl2的id为.impl.Impl2。(注意前导点号)
  • NAME:通过某个属性来区分标识不同类型。在序列化时,属性值为类的Name。
  • CUSTOM:自定义识别码,由@JsonTypeIdResolver解析。
include解释
  • PROPERTY:作为数据的属性的兄弟属性(即平级的)
  • EXISTING_PROPERTY:作为POJO中已经存在的属性。反序列化时与PROPERTY一样的处理逻辑。在序列化时,TypeSerializer不起作用。
  • EXTERNAL_PROPERTY:作为扩展属性。与PROPERTY类似,除了在层级结构的第一层,仅放在属性上而不是类上。
  • WRAPPER_OBJECT:作为一个包装的对象,即对象的现有属性作为一个包装对象的属性。
  • WRAPPER_ARRAY:作为一个包装的数组
property解释

property属性只有当:

  • use为:JsonTypeInfo.Id.CLASSJsonTypeInfo.Id.MINIMAL_CLASSJsonTypeInfo.Id.NAME

  • include为:JsonTypeInfo.As.PROPERTY、JsonTypeInfo.As.EXISTING_PROPERTY、JsonTypeInfo.As.EXTERNAL_PROPERTY

时才有效。

@JsonTypeInfo在属性(字段,方法)上使用时,此注解适用于值。 当在集合类型(List,Map,Array)上使用时,它将应用于元素,而不是集合本身。 对于非集合类型,没有区别。

@JsonTypeName

指定用于带注解的类的逻辑类型名称。如果在@JsonSubTypes.Type中定义了 Name 属性,这优先使用它,否则使用@JsonTypeName的值。

@JsonSubTypes

指定带注解类型的子类型。

@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE, ElementType.FIELD,
    ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonSubTypes {
    /**  子类型列表 */
    public Type[] value();
 
    public @interface Type {
        /** 子类型的Class */
        public Class<?> value();

        /**
         * 定义在类型上的逻辑名称 
         */
        public String name() default "";
    }
}

全局Default Typing机制

除了使用@JsonTypeInfo注解来实现多态数据绑定,还可以使用全局Default Typing机制。

DefaultTyping有四个选项:

  • JAVA_LANG_OBJECT: 当对象属性类型为Object时生效
  • OBJECT_AND_NON_CONCRETE: 当对象属性类型为Object或者非具体类型(抽象类和接口)时生效
  • NON_CONCRETE_AND+_ARRAYS: 同上, 另外所有的数组元素的类型都是非具体类型或者对象类型
  • NON_FINAL: 对所有非final类型或者非final类型元素的数组

对象引用与标识

@JsonManagedReference@JsonBackReference注解用于处理父/子关系并解决循环问题。

@JsonManagedReference

@JsonBackReference

@JsonIdentityInfo

用于指定在序列化/反序列化值时使用对象标识,例如,处理无限递归类型的问题。

@JsonIdentityReference

元注解

@JacksonAnnotation

注解在其他注解上,用于标识是jackson定义的注解。

@JacksonAnnotationsInside

AnnotationIntrospector

AnnotationIntrospector用于在处理数据时根据注解来自定义处理逻辑。默认的实现为NopAnnotationIntrospector,不做任何处理。

还有一个 JacksonAnnotationIntrospector,用于处理Jackson的注解逻辑。

image-20230223092607727

AnnotationIntrospector抽象类实现了很多空方法,子类覆盖具体方法即可实现自定义逻辑。大部分方法都有一个类型为的Annotated参数。

image-20230223093233468

示例:实现某个字段返回 **** 代替


@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
class Person {
    @Mask
    private String name;
    private int age;
    private boolean male;

    private Address address;
}

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
class Address {
    @Mask
    private String detail;
    private String post;
}

1、定义注解

@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@interface Mask {
    String value() default "****";
}

2、定义Serializer 用于序列化


class DataMaskingSerializer extends StdScalarSerializer<Object> {
    private String mask = "null";
    
    public DataMaskingSerializer(String mask) {
        super(String.class, false);
        this.mask = mask;
    }

    /**
    参数 o:当前用于序列化的对象,
    */
    @Override
    public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        //只要加了注解,就用mask 代替
        jsonGenerator.writeString(mask);
    }
}

3、定义AnnotationIntrospector 用于指定注解要使用哪个Serializer


class PersonAnnotationIntrospector extends NopAnnotationIntrospector {

    @Override
    public Object findSerializer(Annotated am) {
        //找到指定注解,则使用指定的序列化器。
        Mask annotation = am.getAnnotation(Mask.class);
        if (annotation != null) {
            return new DataMaskingSerializer(annotation.value());
        }
        //返回null,则表示使用默认的(或者super)指定的序列化器
        return null;
    }
}

4、objectMapper设置 AnnotationIntrospector

        Address address = new Address("this is my home.", "410074");
        Person person = new Person("demon", 18, true, address);

        ObjectMapper objectMapper = new ObjectMapper();
        PersonAnnotationIntrospector ai = new PersonAnnotationIntrospector();
        objectMapper.setAnnotationIntrospector(ai);
        String s = objectMapper.writeValueAsString(person);
        System.out.println(s);

5、输出结果:

{"name":"****","age":18,"male":true,"address":{"detail":"****","post":"410074"}}

AnnotationIntrospectorPair

用于组合已有的AnnotationIntrospector 。可以设置主要 的和次要的。例如可以组合 JacksonAnnotationIntrospector ,用于满足现有的逻辑,再扩展自定义逻辑。



@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
class Person {
    @Mask
    private String name;

    @JsonFormat(pattern = "YYYY-MM-dd",shape = JsonFormat.Shape.STRING)
    Date birthday;
    private boolean male;

    private Address address;
}

仅自定义实现

ObjectMapper objectMapper = new ObjectMapper();
        AnnotationIntrospector ai = objectMapper.getSerializationConfig().getAnnotationIntrospector();
        AnnotationIntrospector newAi = AnnotationIntrospectorPair.pair(ai, new DataMaskingAnnotationIntrospector());
        objectMapper.setAnnotationIntrospector(newAi);
        return objectMapper;

假设只设置自定义AnnotationIntrospector,则默认的注解识别不了。



    public static void test() throws JsonProcessingException {
        Address address = new Address("this is my home.", "410074");
        Person person = new Person("demon",   Date.from(Instant.now()), true, address);

        ObjectMapper objectMapper = new ObjectMapper();
        PersonAnnotationIntrospector ai = new PersonAnnotationIntrospector();
        objectMapper.setAnnotationIntrospector(ai);
        String s = objectMapper.writeValueAsString(person);
        System.out.println(s);
    }

输出:

{"name":"****","birthday":1677122919273,"male":true,"address":{"detail":"****","post":"410074"}}

@JsonFormat未起作用。

组合已有实现

   public static void test() throws JsonProcessingException {

        Address address = new Address("this is my home.", "410074");
        Person person = new Person("demon",   Date.from(Instant.now()), true, address);

        ObjectMapper objectMapper = new ObjectMapper();
        PersonAnnotationIntrospector ai = new PersonAnnotationIntrospector();
		//组合多个实现
       //默认的是:### JacksonAnnotationIntrospector
        AnnotationIntrospector annotationIntrospector = objectMapper.getSerializationConfig().getAnnotationIntrospector();
        AnnotationIntrospector newAi = AnnotationIntrospectorPair.pair(annotationIntrospector, ai);

        objectMapper.setAnnotationIntrospector(newAi);
        String s = objectMapper.writeValueAsString(person);
        System.out.println(s);
    }

输出:

{"name":"****","birthday":"2023-02-23","male":true,"address":{"detail":"****","post":"410074"}}

JacksonAnnotationIntrospector

Jackson默认的实现。

支持的用于序列化注解:

  • JsonSerialize.class,
  • JsonView.class,
  • JsonFormat.class,
  • JsonTypeInfo.class,
  • JsonRawValue.class,
  • JsonUnwrapped.class,
  • JsonBackReference.class,
  • JsonManagedReference.class

支持的用于反序列化注解:

  • JsonDeserialize.class,
  • JsonView.class,
  • JsonFormat.class,
  • JsonTypeInfo.class,
  • JsonUnwrapped.class,
  • JsonBackReference.class,
  • JsonManagedReference.class,
  • JsonMerge.class // since 2.9

附录

参考

json中文官网:http://www.json.org/json-zh.html

json官网:http://www.json.org/

json参考手册:(译) JSON-RPC 2.0 规范(中文版)

Jackson官网地址:https://github.com/FasterXML/jackson

Jackson文档地址:https://github.com/FasterXML/jackson-docs

高级应用:https://www.baeldung.com/jackson-advanced-annotations

教程:

https://github.com/FasterXML/jackson-databind/

https://www.baeldung.com/jackson

https://mkyong.com/tutorials/java-json-tutorials/

https://jenkov.com/tutorials/java-json/index.html

https://www.logicbig.com/tutorials/misc/jackson.html

自定义注解

多态类型反序列化示例

use 属性 Id.CLASS


public class PTypeTest {
    public static void main(String[] args) {
        try {
            test();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void test() throws IOException {
        ObjectMapper objectMapper = new ObjectMapper();
        List<Shape> shapes = Lists.newArrayList(
                new Rectangle(10, 20),
                new Circle(100),
                new Square(50)
        );

        // serializing
        String jsonString = objectMapper.writeValueAsString(new Containers(shapes));
        System.out.println("jsonString=" + jsonString);
        System.out.println();

        // deserializing
        Containers containers = objectMapper.readValue(jsonString, Containers.class);
        System.out.println("containers=" + containers);
    }
}

@JsonTypeInfo(
        use = JsonTypeInfo.Id.CLASS,
        include = JsonTypeInfo.As.PROPERTY,
        property = "cn"
)
abstract class Shape {
    private String name;
    private int age;
}

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
class Rectangle extends Shape {

    private int length;

    private int width;
}

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
class Square extends Shape {
    private int sideLength;
}

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
class Circle extends Shape {
    private int radius;
}

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
class Containers {
    private List<Shape> shapes;
}

输出:

属性的名字为 :className,属性值为类的全限定名称

jsonString={"shapes":[{"cn":"demon.research.objectmapper.Rectangle","length":10,"width":20},{"cn":"demon.research.objectmapper.Circle","radius":100},{"cn":"demon.research.objectmapper.Square","sideLength":50}]}
 
containers=demon.research.objectmapper.Containers@661972b0

@JsonTypeInfo 也可以放在属性上。

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
class Containers {
    @JsonTypeInfo(
            use = JsonTypeInfo.Id.CLASS,
            include = JsonTypeInfo.As.PROPERTY,
            property = "cn2"
    )
    private List<Shape> shapes;
}

输出:

jsonString={"shapes":[{"cn2":"demon.research.objectmapper.Rectangle","length":10,"width":20},{"cn2":"demon.research.objectmapper.Circle","radius":100},{"cn2":"demon.research.objectmapper.Square","sideLength":50}]}

属性上的注解优先级高于类上的注解。

use 属性 Id.MINIMAL_CLASS

@JsonTypeInfo(
        use = JsonTypeInfo.Id.MINIMAL_CLASS,
        include = JsonTypeInfo.As.PROPERTY,
        property = "cn"
)
abstract class Shape {
    private String name;
    private int age;
}

输出:

jsonString={"shapes":[{"cn":".Rectangle","length":10,"width":20},{"cn":".Circle","radius":100},{"cn":".Square","sideLength":50}]}

use 属性 Id.NAME


@JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        include = JsonTypeInfo.As.PROPERTY,
        property = "cn"
)
abstract class Shape {
    private String name;
    private int age;
}

输出:

jsonString={"shapes":[{"cn":"Rectangle","length":10,"width":20},{"cn":"Circle","radius":100},{"cn":"Square","sideLength":50}]}

com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Could not resolve type id 'Rectangle' as a subtype of [simple type, class demon.research.objectmapper.Shape]: known type ids = [] (for POJO property 'shapes')
 at [Source: (String)"{"shapes":[{"cn":"Rectangle","length":10,"width":20},{"cn":"Circle","radius":100},{"cn":"Square","sideLength":50}]}";

反序列化时,通过属性值识别不了类。需要加用于识别类的属性值。

@JsonSubTypes(
        { @JsonSubTypes.Type(value = Rectangle.class, name = "Rectangle")
        ,@JsonSubTypes.Type(value = Square.class, name = "Square")
        ,@JsonSubTypes.Type(value = Circle.class, name = "Circle")
        }
       
)
abstract class Shape {}

输出:

jsonString={"shapes":[{"cn":"Rectangle","length":10,"width":20},{"cn":"Circle","radius":100},{"cn":"Square","sideLength":50}]}

containers=demon.research.objectmapper.Containers@6356695f

include 属性 As.WRAPPER_OBJECT

现有属性作为一个包装对象的属性。包装属性的名称为 Name。


@JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        include = JsonTypeInfo.As.WRAPPER_OBJECT,
        property = "cn"
)
@JsonSubTypes(
        { @JsonSubTypes.Type(value = Rectangle.class ,  name = "rectangle")
                ,@JsonSubTypes.Type(value = Square.class, name = "square")
                ,@JsonSubTypes.Type(value = Circle.class, name = "circle")
        }

)

@Getter
@Setter
abstract class Shape {
    public String type;
}

输出:

jsonString={"shapes":[{"rectangle":{"type":null,"length":10,"width":20}},{"circle":{"type":null,"radius":100}},{"square":{"type":null,"sideLength":50}}]}

include 属性 As.WRAPPER_ARRAY

现有对象与 类型区分属性(一个字符串对象) 作为2个对象,在一个数组里。

@JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        include = JsonTypeInfo.As.WRAPPER_ARRAY,
        property = "cn"
)
@JsonSubTypes(
        { @JsonSubTypes.Type(value = Rectangle.class ,  name = "rectangle")
                ,@JsonSubTypes.Type(value = Square.class, name = "square")
                ,@JsonSubTypes.Type(value = Circle.class, name = "circle")
        }

)

@Getter
@Setter
abstract class Shape {
    public String type;
}

输出:

jsonString={"shapes":[["rectangle",{"type":null,"length":10,"width":20}],["circle",{"type":null,"radius":100}],["square",{"type":null,"sideLength":50}]]}

@JsonAppend示例

使用attrs属性

@Data
@JsonAppend(attrs = {@JsonAppend.Attr(value = "age"),@JsonAppend.Attr(value = "sex")})
public class User {
    private String id;
    private String userName;
    private String passWord;
    private String email;
    private String nickName;
    private Date createTime;
}

{
  "id" : "999",
  "userName" : "小五",
  "passWord" : "888888",
  "email" : "111111@qq.com",
  "nickName" : null,
  "createTime" : 1675151635581,
    // 新增的2个属性。
  "age" : "20",
  "sex" : "男"
}

使用props属性

@Data
@JsonAppend(props = {@JsonAppend.Prop(value =TestWriter.class ,type = String.class,name = "idcard")}, prepend = true)
public class User {
    private String id;
    private String userName;
    private String passWord;
    private String email;
    private String nickName;
    private Date createTime;
}

@NoArgsConstructor
class TestWriter extends VirtualBeanPropertyWriter {


    private TestWriter(BeanPropertyDefinition propDef, Annotations contextAnnotations, JavaType declaredType) {
        super(propDef, contextAnnotations, declaredType);
    }


    @Override
    protected Object value(Object bean, JsonGenerator gen, SerializerProvider prov) throws Exception {
        return "5002001564564565";
    }

    @Override
    public VirtualBeanPropertyWriter withConfig(MapperConfig<?> config, AnnotatedClass declaringClass, BeanPropertyDefinition propDef, JavaType type) {
        return new TestWriter(propDef, declaringClass.getAnnotations(), type);
    }
}

{
    //增加了 idcard属性。
  "idcard" : "5002001564564565",
  "id" : "999",
  "userName" : "小五",
  "passWord" : "888888",
  "email" : "111111@qq.com",
  "nickName" : null,
  "createTime" : 1675151702894
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/367506.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2022IDEA搭建springMvc项目

springmvc项目搭建一. 创建maven项目二. Add Framework Support三. 添加依赖并配置maven四. 配置前端控制器DispatcherServlet五. 配置SpringMVC.XML文件六. 创建controller类七. 创建index.html页面八. 查看jar包是否添加九. 配置tomcat&#xff08;重&#xff09;十. springm…

Kafka(7):生产者详解

1 消息发送 1.1 Kafka Java客户端数据生产流程解析 1 首先要构造一个 ProducerRecord 对象,该对象可以声明主题Topic、分区Partition、键 Key以及值 Value,主题和值是必须要声明的,分区和键可以不用指定。 2 调用send() 方法进行消息发送。 3 因为消息要到网络上进行传输…

国产蓝牙耳机什么便宜又好用?学生党平价蓝牙耳机推荐

蓝牙耳机凭借近几年的快速发展&#xff0c;越来越多的品牌、款式出现在人们的日常生活当中。最近看到很多人问&#xff0c;国产蓝牙耳机什么便宜又好用&#xff1f;针对这个问题&#xff0c;我来给大家推荐几款平价蓝牙耳机&#xff0c;很适合学生党&#xff0c;一起来看看吧。…

推荐系统从入门到入门(3)——基于MapReuduce与Spark的分布式推荐系统构建

本系列博客总结了不同框架、不同算法、不同界面的推荐系统&#xff0c;完整阅读需要大量时间&#xff08;又臭又长&#xff09;&#xff0c;建议根据目录选择需要的内容查看&#xff0c;欢迎讨论与指出问题。 目录 系列文章梗概 系列文章目录 三、MapReduce 1.MapReduce详…

赶紧收藏:如何使用Telegram客户支持

想要使用Telegram需要客户支持&#xff1f;您需要了解的有关使用Telegram作为客户服务渠道的所有信息&#xff0c;本文章都会介绍。我们将首先讨论提供Telegram支持以及入门所需了解的内容。然后&#xff0c;我们将向您展示如何用智能客服工具ss可以帮助您提供一流的服务Telegr…

Oracle监听详解

本文摘自《ORACLE数据库技术实用详解》和《成功之路&#xff1a;ORACLE 11g学习笔记》 配置网络环境 本文将介绍和Oracle相关的网络问题&#xff0c;Oracle网络建立在操作系统之上。配置操作系统网络是配置Oracle网络的第一步。在配置Oracle网络之前&#xff0c;我们需要确保操…

数学基础--均值、方差、标准差、协方差

1. 简介 统计学中最核心的概念之一是&#xff1a;标准差及其与其他统计量&#xff08;如方差和均值&#xff09;之间的关系&#xff0c;本文将对标准差这一概念提供直观的视觉解释&#xff0c;在文章的最后我们将会介绍协方差的概念。 2. 概念介绍 均值 均值&#xff1a; 均值…

(一)Spring-Cloud源码分析之核心流程关系及springcloud与springboot包区别(新)

文章目录1. 前言2. springcloud简介3. Springcloud包简介4. Springcloud和Springboot流程关系5. Springcloud启动流程新增的功能和接口5.1 新增接口5.2 新增功能类5.2.1 spring-cloud-context包5.2.2 spring-cloud-commons包6. Springcloud实现机制带来的问题7. Springcloud和S…

【MyBatis】映射器配置|注解完成CRUD(三)

&#x1f697;MyBatis学习第三站~ &#x1f6a9;起始站&#xff1a;MyBatis概述&环境搭建(一) &#x1f6a9;本文已收录至专栏&#xff1a;数据库学习之旅 &#x1f44d;希望您能有所收获 上一篇我们学习了如何使用Mapper代理开发&#xff0c;核心配置文件&#xff0c;但却…

OnlyOffice验证(一)DocumentServer编译验证

OnlyOffice验证&#xff08;一&#xff09;DocumentServer编译验证 资源准备 Ubuntu16.04桌面版 验证用的版本[ubuntu-16机接上传ubuntu.04.7-desktop-amd67131.iso&#xff0c;&#xff08;别用高版本&#xff01;试过20.04耽误两三天&#xff0c;差点放弃了&#xff09;&am…

javaee之node.js与es6

问题1&#xff1a;在IDEA控制台为什么node显示不会出来命令 修改完之后记得重新启动电脑 问题2&#xff1a;response.end()作用 在Web开发中&#xff0c;浏览器端的请求到达服务器进行处理的时候&#xff0c;Response.End的作用就是让request执行到此结束&#xff0c;输出到客户…

移掉K位数字-力扣402-java贪心策略

一、题目描述给你一个以字符串表示的非负整数 num 和一个整数 k &#xff0c;移除这个数中的 k 位数字&#xff0c;使得剩下的数字最小。请你以字符串形式返回这个最小的数字。示例 1 &#xff1a;输入&#xff1a;num "1432219", k 3输出&#xff1a;"1219&q…

Vue实战第5章:发布Vue工程到github静态页面

前言 本篇在讲什么 简单讲解关于Vue发布github静态页面相关的内容 本篇适合什么 适合初学Vue的小白 适合想要自己搭建网站的新手 本篇需要什么 对Html和css语法有简单认知 对Vue有简单认知 Node.js(博主v18.13.0)的开发环境 Npm(博主v8.19.3)的开发环境 Vue(博主v5.…

< elementUI组件样式及功能补全: 实现点击steps组件跳转对应步骤 >

文章目录&#x1f449; 前言&#x1f449; 一、效果演示&#x1f449; 二、点击steps跳转效果实现&#x1f449; 三、实现案例往期内容 &#x1f4a8;&#x1f449; 前言 在 Vue elementUi 开发中&#xff0c;elementUI中steps步骤条组件只提供了change方法&#xff0c;并未提…

【Database-03】从 MySQL 迁移到 达梦数据库(DM 8)

1、环境 源数据库 MySQL 8.30 目标数据库 DM 8 操作系统 Centos 9 Steam 迁移工具 DM 数据迁移工具 (DM DTS) 2、开始迁移 2.1、打开DM数据迁移工具 在新建工程对话框中填写工程名和工程描述信息&#xff0c;点击【确定】按钮&#xff0c;成功添加了一个工程。 2.2、新建迁…

海思SD3403/SS928V100开发(5)MIPI_YUV相机vio sample开发----修改思路

1. 前言 sensor输出格式: YUV422 8bit 硬件连接: MIPI_YUV相机(4lane MIPI) -> SS928V100 MIPI0(4lane) 框图: 2. 几个问题 基于SS928 SDK中的 vio sample修改; 但是sample里面都是基于RAW RGB sensor开发的sample, 没有现成的MIPI_YUV sensor的参考,需要自己…

[黑马程序员SSM框架教程] Spring-11-setter注入

思考&#xff1a;向一个类中传递数据要几种&#xff1f; set方法构造方法 思考&#xff1a;依赖注入描述了在容器中建立bean与bean之间依赖关系的过程&#xff0c;如果bean运行需要数字或字符呢 引用类型简单类型&#xff08;基本数据类型和字符串&#xff09; 注入方式&#x…

软考学习笔记(题目知识记录) 2023.2.24

答案为 概要设计阶段 本题涉及软件工程的概念 软件工程的任务是基于需求分析的结果建立各种设计模型&#xff0c;给出问题的解决方案 软件设计可以分为两个阶段&#xff1a; 概要设计阶段和详细设计阶段 结构化设计方法中&#xff0c;概要设计阶段进行软件体系结构的设计&…

webpack基础学习,各个loader和plugin的具体配置

一、邂逅Webpack Webpack是什么 webpack是一个静态的模块化打包工具&#xff0c;为现代的JavaScript应用程序&#xff1b; 打包bundler&#xff1a;webpack可以将帮助我们进行打包&#xff0c;所以它是一个打包工具 静态的static&#xff1a;这样表述的原因是我们最终可以将…

(三十)大白话MySQL的redo log buffer中的缓冲日志,到底什么时候可以写入磁盘?

之前我们给大家讲解了一下redo log buffer的缓冲机制&#xff0c;大家现在应该都知道了&#xff0c;redo log在写的时候&#xff0c;都是一个事务里的一组redo log&#xff0c;先暂存在一个地方&#xff0c;完事儿了以后把一组redo log写入redo log buffer。 写入redo log buf…