1. 概述
在这个教程中,我们将了解Gson库中的@Expose
和@SerializedName
注解。@Expose
帮助控制类属性在序列化或反序列化过程中的处理,而@SerializedName
则用于映射对象属性名称与JSON字符串中的键名,反之亦然。
2. @Expose
有些情况下,类属性的某些敏感值不应被序列化或转换为JSON字符串。为此,Gson提供了@Expose
注解,它有两个布尔属性:serialize
和deserialize
。
假设Person
类中的password
属性因为敏感信息不应被序列化,我们应使用@Expose(serialize=false)
装饰该属性:
public class Person {
@Expose(serialize = true)
private String firstName;
@Expose(serialize = true)
private String lastName;
@Expose()
private String emailAddress;
@Expose(serialize = false)
private String password;
@Expose(serialize = true)
private List<BankAccount> bankAccounts;
//General getters and setters..
}
类似地,BankAccount
对象中的accountNumber
属性也因为敏感信息不应被序列化,同样需要使用@Expose(serialize=false)
:
public class BankAccount {
@Expose(serialize = false, deserialize = false)
private String accountNumber;
@Expose(serialize = true, deserialize = true)
private String bankName;
//general getters and setters..
}
现在,当我们想要将对象转换为JSON字符串时,不能直接使用默认的Gson
对象(通过new
关键字创建),而是需要使用GsonBuilder
类,通过配置excludeFieldsWithoutExposeAnnotation()
来实例化具有此设置的Gson
类。
让我们看看PersonSerializer
类:
public class PersonSerializer {
private static final Gson configuredGson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
private static final Gson defaultGson = new Gson();
public static String serializeWithConfiguredGson(Person person) {
return configuredGson.toJson(person);
}
public static String serializeWithDefaultGson(Person person) {
return defaultGson.toJson(person);
}
}
测试serializeWithConfiguredGson()
方法:
public class PersonSerializerUnitTest {
@Test
public void whenUseCustomGson_thenDonotSerializeAccountNumAndPassword () {
String personJson = PersonSerializer.serializeWithConfiguredGson(person);
assertFalse("Test failed: password found", personJson.contains("password"));
assertFalse("Test failed: account number found", personJson.contains("accountNumber:"));
}
}
如预期,输出中并未包含敏感属性password
和accountNumber
:
{
"firstName":"Parthiv",
"lastName":"Pradhan","email":"[email protected]",
"bankAccounts":[{"bankName":"Bank of America"},{"bankName":"Bank of America"}]
}
同样,测试serializeWithDefaultGson()
方法:
@Test
public void whenUseDefaultGson_thenSerializeAccountNumAndPassword () {
String personJson = PersonSerializer.serializeWithDefaultGson(person);
assertTrue("Test failed: password not found", personJson.contains("password"));
assertTrue("Test failed: account number not found", personJson.contains("accountNumber"));
}
正如之前所述,defaultGson
对象无法识别@Expose
注解,结果输出了password
和accountNumber
:
{
"firstName":"James","lastName":"Cameron","email":"[email protected]",
"password":"secret",
"bankAccounts":
[
{"accountNumber":"4565432312","bankName":"Bank of America"},
{"accountNumber":"4565432616","bankName":"Bank of America"}
]
}
对于更复杂的场景,要排除属性的序列化,可以使用ExclusionStrategy
(详情参阅/gson-exclude-fields-serialization#exclusionstrategy)。
3. @SerializedName
@SerializedName
注解相当于一个自定义转换器。通常,我们会先将对象转换为JSON字符串,然后修改其属性键,再作为参数发送到Web服务。
同样,在将JSON字符串转换为对象时,我们需要将其属性键映射到对象的属性名称。Gson库利用@SerializedName
这个单一注解巧妙地结合了这两个步骤,多么简洁!
很多时候,我们在序列化时希望生成尽可能小的payload。让我们尝试使用一些比属性名称短得多的自定义键名序列化Country
类:
public class Country {
@SerializedName(value = "name")
private String countryName;
@SerializedName(value = "capital")
private String countryCapital;
@SerializedName(value = "continent")
private String continentName;
//general getters and setters..
}
现在,将Country
对象转换为JSON:
public class PersonSerializer {
private static final Gson defaultGson = new Gson();
public static String toJsonString(Object obj) {
return defaultGson.toJson(obj);
}
}
检查方法是否有效:
@Test
public void whenUseSerializedAnnotation_thenUseSerializedNameinJsonString() {
String countryJson = PersonSerializer.toJsonString(country);
logger.info(countryJson);
assertFalse("Test failed: No change in the keys", countryJson.contains("countryName"));
assertFalse("Test failed: No change in the keys", countryJson.contains("contentName"));
assertFalse("Test failed: No change in the keys", countryJson.contains("countryCapital"));
assertTrue("Test failed: No change in the keys", countryJson.contains("name"));
assertTrue("Test failed: No change in the keys", countryJson.contains("continent"));
assertTrue("Test failed: No change in the keys", countryJson.contains("capital"));
}
正如预期,我们发现属性键与注解中提供的名称匹配:
{"name":"India","capital":"New Delhi","continent":"Asia"}
让我们看看这个注解是否能帮助将上述JSON转换为Country
对象。为此,我们可以使用fromJsonString()
方法:
public class PersonSerializer {
private static final Gson defaultGson = new Gson();
public static Country fromJsonString(String json) {
return defaultGson.fromJson(json, Country.class);
}
}
检查方法是否工作:
@Test
public void whenJsonStrCreatedWithCustomKeys_thenCreateObjUsingGson() {
String countryJson = PersonSerializer.toJsonString(country);
Country country = PersonSerializer.fromJsonString(countryJson);
assertEquals("Fail: Object creation failed", country.getCountryName(), "India");
assertEquals("Fail: Object creation failed", country.getCountryCapital(), "New Delhi");
assertEquals("Fail: Object creation failed", country.getContinentName(), "Asia");
}
方法能够创建Country
对象:
Country{countryName='India', countryCapital='New Delhi', continentName='Asia'}
4. 总结
在这篇文章中,我们学习了Gson库中的两个重要注解:@Expose
和@SerializedName
。它们的功能完全不同,如文中所示。文章中使用的代码示例可在GitHub上找到。