1. 概览
在本教程中,我们将深入探讨 Jackson 注解。
我们将看到如何使用现有的注解、如何创建自定义注解,最后如何禁用它们。
2. Jackson 序列化注解
首先,我们来看一下序列化注解。
2.1. @JsonAnyGetter
@JsonAnyGetter 注解允许将 Map 字段用作标准属性,从而提供更大的灵活性。
例如,ExtendableBean 实体具有 name 属性以及以键/值对形式的一组可扩展属性:
public class ExtendableBean {
public String name;
private Map<String, String> properties;
@JsonAnyGetter
public Map<String, String> getProperties() {
return properties;
}
}
当我们序列化这个实体的实例时,我们会得到 Map 中的所有键值对作为标准的普通属性:
{
"name":"My bean",
"attr2":"val2",
"attr1":"val1"
}
这是该实体的序列化过程如下:
@Test
public void whenSerializingUsingJsonAnyGetter_thenCorrect()
throws JsonProcessingException {
ExtendableBean bean = new ExtendableBean("My bean");
bean.add("attr1", "val1");
bean.add("attr2", "val2");
String result = new ObjectMapper().writeValueAsString(bean);
assertThat(result, containsString("attr1"));
assertThat(result, containsString("val1"));
}
我们也可以将 optional 参数设置为 false 来禁用 @JsonAnyGetter()。在这种情况下,Map 将会被转换成 JSON,并在序列化后出现在 properties 变量下。
2.2. @JsonGetter
@JsonGetter 注解是@JsonProperty 注解的替代品,用于标记一个 getter 方法。
在以下示例中,我们将方法 getTheName()指定为 MyBean 实体的 name 属性的 getter 方法:
public class MyBean {
public int id;
private String name;
@JsonGetter("name")
public String getTheName() {
return name;
}
}
这便是其实现方式:
@Test
public void whenSerializingUsingJsonGetter_thenCorrect()
throws JsonProcessingException {
MyBean bean = new MyBean(1, "My bean");
String result = new ObjectMapper().writeValueAsString(bean);
assertThat(result, containsString("My bean"));
assertThat(result, containsString("1"));
}
2.3. @JsonPropertyOrder
我们可以使用@JsonPropertyOrder 注解来指定序列化时属性的顺序。
让我们为 MyBean 实体的属性设置自定义排序:
@JsonPropertyOrder({ "name", "id" })
public class MyBean {
public int id;
public String name;
}
这里是序列化的输出:
{
"name":"My bean",
"id":1
}
然后我们可以进行一个简单的测试:
@Test
public void whenSerializingUsingJsonPropertyOrder_thenCorrect()
throws JsonProcessingException {
MyBean bean = new MyBean(1, "My bean");
String result = new ObjectMapper().writeValueAsString(bean);
assertThat(result, containsString("My bean"));
assertThat(result, containsString("1"));
}
我们也可以使用@JsonPropertyOrder(alphabetic=true)将属性按字母顺序排列。在这种情况下,序列化的输出将是:
{
"id":1,
"name":"My bean"
}
2.4. @JsonRawValue
@JsonRawValue 注解可以指示 Jackson 按原样序列化一个属性。
在以下示例中,我们使用@JsonRawValue 注解嵌入一些自定义 JSON 作为实体的值:
public class RawBean {
public String name;
@JsonRawValue
public String json;
}
序列化实体的输出是:
{
"name":"My bean",
"json":{
"attr":false
}
}
接下来,这里有一个简单的测试:
@Test
public void whenSerializingUsingJsonRawValue_thenCorrect()
throws JsonProcessingException {
RawBean bean = new RawBean("My bean", "{\"attr\":false}");
String result = new ObjectMapper().writeValueAsString(bean);
assertThat(result, containsString("My bean"));
assertThat(result, containsString("{\"attr\":false}"));
}
我们还可以使用可选的布尔参数值,该值定义此注解是否启用。
2.5. @JsonValue
@JsonValue 表示库将使用该方法来序列化整个实例。
例如,在一个枚举中,我们使用@JsonValue 注解 getName 方法,以便此类实体在序列化时会通过其名称进行表示:
public enum TypeEnumWithValue {
TYPE1(1, "Type A"), TYPE2(2, "Type 2");
private Integer id;
private String name;
// standard constructors
@JsonValue
public String getName() {
return name;
}
}
现在我们来测试一下:
@Test
public void whenSerializingUsingJsonValue_thenCorrect()
throws JsonParseException, IOException {
String enumAsString = new ObjectMapper()
.writeValueAsString(TypeEnumWithValue.TYPE1);
assertThat(enumAsString, is(""Type A""));
}
2.6. @JsonRootName
@JsonRootName 注解用于启用包装时,指定要使用的根包裹名称。
包装意味着,而不是将 User 序列化为类似于:
{
"id": 1,
"name": "John"
}
它将会像这样包装:
{
"User": {
"id": 1,
"name": "John"
}
}
那么我们来看一个例子。我们将使用@JsonRootName 注解来指示这个潜在的包装实体的名字:
@JsonRootName(value = "user")
public class UserWithRoot {
public int id;
public String name;
}
默认情况下,包装器的名称会是类名——UserWithRoot。通过使用注解,我们可以得到更整洁的 user:
@Test
public void whenSerializingUsingJsonRootName_thenCorrect()
throws JsonProcessingException {
UserWithRoot user = new User(1, "John");
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.WRAP_ROOT_VALUE);
String result = mapper.writeValueAsString(user);
assertThat(result, containsString("John"));
assertThat(result, containsString("user"));
}
这里是序列化的输出:
{
"user":{
"id":1,
"name":"John"
}
}
从 Jackson 2.4 开始,数据格式(例如 XML)中新增了一个可选参数 namespace。如果我们添加它,将会成为完全限定名的一部分:
@JsonRootName(value = "user", namespace="users")
public class UserWithRootNamespace {
public int id;
public String name;
// ...
}
如果使用 XmlMapper 进行序列化,输出将是:
<user xmlns="users">
<id xmlns="">1</id>
<name xmlns="">John</name>
<items xmlns=""/>
</user>
2.7. @JsonSerialize
@JsonSerialize 表示在序列化实体时使用自定义的序列化器。
让我们来看一个简单的例子。我们将使用@JsonSerialize 注解,使用 CustomDateSerializer 序列化 eventDate 属性:
public class EventWithSerializer {
public String name;
@JsonSerialize(using = CustomDateSerializer.class)
public Date eventDate;
}
这里是简单的自定义 Jackson 序列化器:
public class CustomDateSerializer extends StdSerializer<Date> {
private static SimpleDateFormat formatter
= new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
public CustomDateSerializer() {
this(null);
}
public CustomDateSerializer(Class<Date> t) {
super(t);
}
@Override
public void serialize(
Date value, JsonGenerator gen, SerializerProvider arg2)
throws IOException, JsonProcessingException {
gen.writeString(formatter.format(value));
}
}
现在我们就在测试中使用这些:
@Test
public void whenSerializingUsingJsonSerialize_thenCorrect()
throws JsonProcessingException, ParseException {
SimpleDateFormat df
= new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
String toParse = "20-12-2014 02:30:00";
Date date = df.parse(toParse);
EventWithSerializer event = new EventWithSerializer("party", date);
String result = new ObjectMapper().writeValueAsString(event);
assertThat(result, containsString(toParse));
}