伴随着 Play1, 我们原来使用的 JSON 库是 Gson. 回忆下 Gson 是怎么自定义序列化对象的 JSON 格式,大概是这样子的
GsonBuilder()..registerTypeHierarchyAdapter(Cat.class, new Cat());
然后 Cat 需要实现 JsonSerializer 的 serialize() 方法。
来到了 Play2 中,JSON 库变成了 Jackson,那么 Jackson 该如何为对象自定义 JSON 格式呢?
例如,默认时 Jackson 对 Map 类型输出的是一个 JSON 对象
Map("key1"->"value1", "key2"->"value2") 转换成 JSON 是 {"key1":"value1", "key2":"value2"}
当为适应某些客户端,对于 LinkedHashMap 类型,我们想要输出的是一个有序的 JSON 数组: [{"key1":"value1"},{"key2":"value2"}]
我们就应该自定义某些 Map 的序列化格式,实现方法有两种,addSerializer 和 @JsonSerialize,不管哪种方式都需事先具体化 JsonSerializer 类,并实现它的 serialize 抽象方法
所以我先来实现一个能序列化 Map 的 JsonArrayMapSerializer 类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
package cc.unmi.serialization; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.databind.*; import java.io.IOException; import java.util.Map; public class JsonArrayMapSerializer extends JsonSerializer<Map<?, ?>> { @Override public void serialize(Map<?, ?> value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException { jgen.writeStartArray(); for(Object key: value.keySet()) { jgen.writeStartObject(); jgen.writeObjectField(key.toString(), value.get(key)); jgen.writeEndObject(); } jgen.writeEndArray(); } } |
上面调用了 JsonGenerator 的 writeXxx() 方法来输出 JSON 格式
接着看两种实现方式
1. addSerializer() 方式
我采用了 Scala 来书写测试代码, 贴代码也有点问题,所以用图片代替
测试结果:
2. @JsonSerialize 注解方式
看下注解 @JsonSerialize 的定义
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.TYPE, ElementType.PARAMETER})
它可以作用在注解,方法,字段,类型和参数上。我们举两个例子,分别是对类型和字段使用此注解
1)自定义的 JsonArrayMap 类型
1 2 3 4 5 6 7 8 9 |
package cc.unmi.serialization; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import java.util.LinkedHashMap; @JsonSerialize(using = JsonArrayMapSerializer.class) public class JsonArrayMap<K, V> extends LinkedHashMap<K, V>{ } |
2) 作用在字段上
1 2 3 4 5 6 7 8 |
val map = new java.util.HashMap[String, String] map.put("key1", "value1") map.put("key2", "value2") val obj = new Object { @JsonSerialize(using = classOf[JsonArrayMapSerializer]) val attr = map } |
用测试用例来验证下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
package cc.unmi.serialization import com.fasterxml.jackson.databind.{PropertyNamingStrategy, ObjectMapper} import utilities.JsonArrayMap import com.fasterxml.jackson.databind.annotation.JsonSerialize import org.scalatest.{MustMatchers, WordSpec} class JsonArrayMapSerializerTest extends WordSpec with MustMatchers { "JsonArrayMapSerializer#serialize" should { val jsonSerializer: ObjectMapper = new ObjectMapper() .setPropertyNamingStrategy(PropertyNamingStrategy.PASCAL_CASE_TO_CAMEL_CASE) "generate a JsonArrayMap as JSON array format" in { val map = new JsonArrayMap[String, String] map.put("key1", "value1") map.put("key2", "value2") val jsonString = jsonSerializer.writeValueAsString(map) jsonString mustBe """[{"key1":"value1"},{"key2":"value2"}]""" } "write Map to json array if annotated with @JsonSerialize(using = classOf[JsonArrayMapSerializer]) " in { val obj = new { @JsonSerialize(using = classOf[JsonArrayMapSerializer]) val field = new java.util.HashMap[String, String] field.put("key1", "value1") field.put("key2", "value2") } val jsonString = jsonSerializer.writeValueAsString(obj) jsonString mustBe """{"Field":[{"key2":"value2"},{"key1":"value1"}]}""" } } } |
测试结果
可以进一步考虑让 @JsonSerialize 应用在别处,如方法参数,getter 方法上。
本文链接 https://yanbin.blog/jackson-custom-serialize-json-format/, 来自 隔叶黄莺 Yanbin Blog
[版权声明] 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。