Jackson的注解

Jackson JSON工具包包含一组Java注释,您可以使用它们来影响如何将JSON读入对象,或者从对象生成JSON。

Read + Write注释

既影响从JSON读取Java对象,也影响将Java对象写入JSON的注解,称为“读取+写入注释”。以下部分更详细地解释了Jackson的读写注释。

@JsonIgnore

Jackson注释@JsonIgnore用于告诉Jackson忽略Java对象的某个属性(字段)。将JSON读入Java对象时,以及将Java对象写入JSON时,都会忽略该属性。示例:

import com.fasterxml.jackson.annotation.JsonIgnore; 
public class PersonIgnore { 
     @JsonIgnore 
     public long personId = 0; 
     public String name = null;
 }

在上面的类中,personId不会从JSON读取属性或将属性写入JSON。

@JsonIgnoreProperties

@JsonIgnorePropertiesJackson注解用于指定一个类的属性忽略的列表。该@JsonIgnoreProperties注释被置于上面的类声明,而不是上面的个人属性(字段),以忽略。示例 :

import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 
 @JsonIgnoreProperties({“firstName”,“lastName”}) 
public class PersonIgnoreProperties { 
     public long personId = 0; 
     public String firstName = null;
     public String lastName = null; 
 }

在此示例中,属性firstNamelastName将被忽略,因为它们的名称列在@JsonIgnoreProperties类声明上方的注释声明中。

@JsonIgnoreType

@JsonIgnoreTypeJackson注释用来标记一个整体类型(类)处处忽视的是使用该类型。这是一个示例,向您展示如何使用@JsonIgnoreType 注释:

import com.fasterxml.jackson.annotation.JsonIgnoreType; 
public class PersonIgnoreType { 
    @JsonIgnoreType 
    public static class Address { 
        public String streetName = null; 
        public String houseNumber = null; 
        public String zipCode = null; 
        public String city = null; 
        public String country = null; 
    } 
     public long PERSONID = 0; 
     public String name = null; 
     public Address address = null; 
 }

在上面的示例中,Address将忽略所有实例。

@JsonAutoDetect

Jackson注释@JsonAutoDetect用于告诉Jackson在Read和Write对象时包含非公开的属性。示例:

import com.fasterxml.jackson.annotation.JsonAutoDetect; 
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) 
public class PersonAutoDetect { 
    private long personId = 123; 
    public String name = null; 
}

JsonAutoDetect.Visibility类包含匹配Java中的可见度常数,这意味着ANYDEFAULTNON_PRIVATENONEPROTECTED_AND_PRIVATEPUBLIC_ONLY

Read注释

只会影响Jackson如何将JSON解析为对象的注解称为“读取注释”。以下部分介绍了Jackson的阅读注释。

@JsonSetter

@JsonSetter在将JSON读入对象时 ,Jackson注释用于告诉Jackson应该将此setter方法的名称与JSON数据中的属性名称相匹配。如果Java类中内部使用的属性名称与JSON文件中使用的属性名称不同,则此选项很有用。

以下Person类使用personId其id属性的名称:

public class Person {

    private long   personId = 0;
    private String name     = null;

    public long getPersonId() { return this.personId; }
    public void setPersonId(long personId) { this.personId = personId; }

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
}

但在此JSON对象中,使用名称id而不是personId

{
  "id"   : 1234,
  "name" : "John"
}

如果没有一些帮助,Jackson无法将id属性从JSON对象映射到personId Java类的字段。
@JsonSetter注释指导Jackson使用setter方法对于给定的JSON场。在我们的例子中,我们@JsonSettersetPersonId()方法上方添加注释。以下是添加@JsonSetter注释的方式:

public class Person {

    private long   personId = 0;
    private String name     = null;

    public long getPersonId() { return this.personId; }
    @JsonSetter("id")
    public void setPersonId(long personId) { this.personId = personId; }

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
}

@JsonSetter注释中 指定的值是与此setter方法匹配的JSON字段的名称。在这种情况下,名称是id因为这是我们要映射到setPersonId()setter方法的JSON对象中的字段的名称。

@JsonAnySetter

Jackson注释@JsonAnySetter指示Jackson为JSON对象中的所有无法识别的字段调用相同的setter方法。“无法识别”是指所有尚未映射到Java对象中的属性或setter方法的字段。看看这Bag堂课:

public class Bag {

    private Map<String, Object> properties = new HashMap<>();

    public void set(String fieldName, Object value){
        this.properties.put(fieldName, value);
    }

    public Object get(String fieldName){
        return this.properties.get(fieldName);
    }
}

然后看看这个JSON对象:

{
  "id"   : 1234,
  "name" : "John"
}

Jackson不能直接映射idname财产此JSON对象到的 Bag类,因为Bag类不包含公共字段或setter方法。

您可以set()通过添加@JsonAnySetter注释告诉Jackson 为所有无法识别的字段调用该方法 ,如下所示:

public class Bag {

    private Map<String, Object> properties = new HashMap<>();

    @JsonAnySetter
    public void set(String fieldName, Object value){
        this.properties.put(fieldName, value);
    }

    public Object get(String fieldName){
        return this.properties.get(fieldName);
    }
}

现在,Jackson将set()使用JSON对象中所有无法识别的字段的名称和值调用该方法。

请记住,这仅对无法识别的字段有影响。例如,如果您向Java类添加了公共name 属性或setName(String)方法Bag,那么name JSON对象中的字段将映射到该属性/ setter。

@JsonCreator

Jackson注释@JsonCreator用于告诉Jackson Java对象有一个构造函数(“创建者”),它可以将JSON对象的字段与Java对象的字段进行匹配。

@JsonSetter的注释不能使用, @JsonCreator在注释中就是十分有用。例如,不可变对象没有任何setter方法,因此需要将它们的初始值注入构造函数中。以这个PersonImmutable类为例:

public class PersonImmutable {

    private long   id   = 0;
    private String name = null;

    public PersonImmutable(long id, String name) {
        this.id = id;
        this.name = name;
    }

    public long getId() {
        return id;
    }

    public String getName() {
        return name;
    }

}

要告诉Jackson它应该调用构造函数,PersonImmutable我们必须将@JsonCreator 注释添加到构造函数中。但仅凭这一点还不够。我们还必须注释构造函数的参数,以告诉Jackson哪些JSON对象的字段传递给哪些构造函数参数。以下是 PersonImmutable类添加@JsonCreator@JsonProperty注释的外观:

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

    public long getId() {
        return id;
    }

    public String getName() {
        return name;
    }

}

请注意构造函数上方的注释和构造函数参数之前的注释。现在Jackson能够PersonImmutable从这个JSON对象创建一个:

{
  "id"   : 1234,
  "name" : "John"
}

@JacksonInject

@JacksonInject用于将值注入已解析的对象,而不是从JSON中读取这些值。例如,假设您正在从各种不同的源下载人JSON对象,并且想知道给定的人对象来自哪个源。原JSON可能不包含该信息,但您可以让Jackson将其注入到从JSON对象创建的Java对象中。

要将Java类中的字段标记为需要由Jackson注入其值@JacksonInject的字段,请在该字段上方添加 注释。这是一个示例PersonInject类,在@JacksonInjectsource字段上方添加了注释:

public class PersonInject {

    public long   id   = 0;
    public String name = null;

    @JacksonInject
    public String source = null;

}

为了让Jackson在source场上注入价值,你需要在创造杰克逊时多做一点ObjectMapper。以下是将Jackson注入Java对象所需的内容:

InjectableValues inject = new InjectableValues.Std().addValue(String.class, "jenkov.com");
PersonInject personInject = new ObjectMapper().reader(inject)
                        .forType(PersonInject.class)
                        .readValue(new File("data/person.json"));

注意如何sourceInjectableValues addValue()方法中设置要注入属性的值。另请注意,该值仅与类型相关联String- 而不是与任何特定字段名称相关联。它是@JacksonInject注释,指定要注入的值的字段。

如果您要从多个源下载人员JSON对象并为每个源注入不同的源值,则必须为每个源重复上述代码。

@JsonDeserialize

@JsonDeserialize用于为Java对象中的给定字段指定自定义反序列化器类。例如,假设你想优化布尔值的上了线的格式falsetrue01

首先,您需要将@JsonDeserialize注释添加到要使用自定义反序列化器的字段中。以下是将@JsonDeserialize注释添加到字段的方式如下:

public class PersonDeserialize {

    public long    id      = 0;
    public String  name    = null;

    @JsonDeserialize(using = OptimizedBooleanDeserializer.class)
    public boolean enabled = false;
}

其次,这里是 注释OptimizedBooleanDeserializer引用的@JsonDeserialize类:

public class OptimizedBooleanDeserializer extends JsonDeserializer<Boolean> {

    @Override
    public Boolean deserialize(JsonParser jsonParser,
            DeserializationContext deserializationContext) throws
        IOException, JsonProcessingException {

        String text = jsonParser.getText();
        if("0".equals(text)) return false;
        return true;
    }
}

请注意,OptimizedBooleanDeserializer该类JsonDeserializer以泛型类型扩展Boolean。这样做会使deserialize()方法返回一个Boolean 对象。如果要反序列化另一种类型(例如a java.util.Date),则必须在泛型括号内指定该类型。
通过调用参数的getText()方法 获取要反序列化的字段的值jsonParser。然后,您可以将该文本反序列化为您的反序列化程序所针对的任何值和类型(Boolean在此示例中为a)。
最后,您需要查看使用自定义反序列化器和@JsonDeserializer注释反序列化对象的样子 :

PersonDeserialize person = objectMapper
        .reader(PersonDeserialize.class)
        .readValue(new File("data/person-optimized-boolean.json"));

注意,我们首先需要创建为读者PersonDeserialize使用类 ObjectMapperreader()方法,然后我们调用readValue() 由该方法返回的对象。

Write注释

影响Jackson如何将Java对象序列化(写入)到JSON的注解叫Write注释。

@JsonInclude

Jackson的注释@JsonInclude告诉Jackson只在某些情况下包括属性。例如,仅当属性为非null,非空或具有非默认值时,才应包含该属性。以下是一个示例,说明如何使用@JsonInclude注释:

import com.fasterxml.jackson.annotation.JsonInclude;
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class PersonInclude {
    public long  personId = 0;
    public String name     = null;
}

此示例仅包含name属性,如果为其设置的值为非空,意味着不为null且不是空字符串。
这个@JsonInclude注释的名称本来可以@JsonIncludeOnlyWhen,但是编写的时间会更长。

@JsonGetter

@JsonGetter注释是用来告诉Jackson某一个字段值应该从调用来获得getter方法,而不是通过直接字段访问。的@JsonGetter ,如果您的Java类使用jQuery的风格的getter和setter名称注释是有用的。例如,代替 getPersonId()setPersonId()你可能有方法personId()personId(long id)
这是一个名为的示例类PersonGetter,它显示了@JsonGetter注释的用法 :

public class PersonGetter {
    private long  personId = 0;
    @JsonGetter("id")
    public long personId() { return this.personId; }
    @JsonSetter("id")
    public void personId(long personId) { this.personId = personId; }
}

如您所见,该personId()方法使用注释进行@JsonGetter注释。@JsonGetter注释上设置的值是应该在JSON对象中使用的名称。因此,personIdJSON对象中使用的名称是id。生成的JSON对象如下所示:

{
    "id":0
}

另请注意,该personId(long personId)方法使用注释进行@JsonSetter 注释,以使Jackson识别出id与JSON对象中的属性匹配的setter 。在@JsonSetter不写Java对象到JSON的时候-从JSON读入的Java对象时注解使用。该@JsonSetter注释只是为了保持完整性的缘故。

@JsonAnyGetter

@JsonAnyGetter可以使用Map作为要序列化JSON性容器。以下是@JsonAnyGetter在Java类中使用注释的示例:

public class PersonAnyGetter {
    private Map<String, Object> properties = new HashMap<>();
    @JsonAnyGetter
    public Map<String, Object> properties() {
        return properties;
    }
}

当看到@JsonAnyGetter注释时,Jackson将从注释Map的方法中获取返回的内容,并将其中的@JsonAnyGetter每个键值对 Map视为属性。换句话说,Map将把所有键值对序列化为JSON作为PersonAnyGetter对象的一部分。

@JsonPropertyOrder

@JsonPropertyOrder杰克逊注解可以应用在哪些命令你的Java对象的字段应该序列化为JSON指定。以下是显示如何使用@JsonPropertyOrder 注释的示例:

@JsonPropertyOrder({"name", "personId"})
public class PersonPropertyOrder {
    public long  personId  = 0;
    public String name     = null;
}

通常,Jackson会PersonPropertyOrder根据他们在课堂上发现的顺序对属性进行序列化。但是,@JsonPropertyOrder注释指定了一个不同的顺序,其中name属性将首先出现,而personId属性在序列化JSON输出中为第二个。

@JsonRawValue

@JsonRawValue注解告诉Jackson,这个属性的值应该直接写,因为它是对JSON输出。如果属性是StringJackson通常会将该值括在引号中,但如果注释@JsonRawValue属性Jackson将不会这样做。

为了更清楚地说明了什么@JsonRawValue,请在没有@JsonRawValue 使用的情况下查看此类:

public class PersonRawValue {

    public long   personId = 0;

    public String address  = "$#";
}

Jackson会将此序列化为此JSON字符串:

{"personId":0,"address":"$#"}

现在我们将属性添加@JsonRawValueaddress属性中,如下所示:

public class PersonRawValue {
    public long   personId = 0;
    @JsonRawValue
    public String address  = "$#";
}

Jackson现在在序列化address财产时省略引号。序列化的JSON因此如下所示:

{"personId":0,"address":$#}

这当然是无效的JSON,那你为什么要这样呢?

好吧,如果address属性包含一个JSON字符串,那么该JSON字符串将作为JSON对象结构的一部分序列化为最终的JSON对象,而不仅仅是addressJSON对象中字段中的字符串 。要了解这将如何工作,让我们address像这样更改属性的值 :

public class PersonRawValue {
    public long   personId = 0;
    @JsonRawValue
    public String address  =
            "{ \"street\" : \"Wall Street\", \"no\":1}";
}

Jackson会把这个序列化为这个JSON:

{"personId":0,"address":{ "street" : "Wall Street", "no":1}}

注意JSON字符串现在是序列化JSON结构的一部分。
如果没有@JsonRawValue注释,Jackson会将对象序列化为此JSON:

{"personId":0,"address":"{ \"street\" : \"Wall Street\", \"no\":1}"}

请注意address属性的值现在如何用引号括起来,并且值内的所有引号都被转义。

@JsonValue

@JsonValue告诉Jackson不应该尝试序列化对象本身,而是在对象上调用一个方法,将对象序列化为JSON字符串。请注意,Jackson将转义自定义序列化返回的String内的任何引号,因此您无法返回例如完整的JSON对象。为此,您应该使用@JsonRawValue(参见上一节)。

@JsonValue注释被添加到杰克逊是调用该对象序列化到一个JSON字符串的方法。以下是显示如何使用@JsonValue注释的示例:

public class PersonValue {
    public long   personId = 0;
    public String name = null;
    @JsonValue
    public String toJson(){
        return this.personId + "," + this.name;
    }
}

要求Jackson序列化PersonValue对象的输出是这样的:

"0,null"

引号由Jackson添加。请记住,对象返回的值字符串中的任何引号都将被转义。

@JsonSerialize

@JsonSerialize注释用于在Java对象指定一个字段的自定义序列。以下是使用@JsonSerialize注释的示例Java类:

public class PersonSerializer {
    public long   personId = 0;
    public String name     = "John";
    @JsonSerialize(using = OptimizedBooleanSerializer.class)
    public boolean enabled = false;
}

注意字段@JsonSerialize上方的注释enabled

OptimizedBooleanSerializer将序列化true的价值1false价值0。这是代码:

public class OptimizedBooleanSerializer extends JsonSerializer<Boolean> {

    @Override
    public void serialize(Boolean aBoolean, JsonGenerator jsonGenerator, 
        SerializerProvider serializerProvider) 
    throws IOException, JsonProcessingException {

        if(aBoolean){
            jsonGenerator.writeNumber(1);
        } else {
            jsonGenerator.writeNumber(0);
        }
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容