对于如下类型定义TestTaskInfo
,props
字段为JSON字符串(这在数据库经常用到),可以自由保存各种类型的数据
java
@Data
public class TestTaskInfo {
private String id;
private String props;
public TestTaskInfo() {
}
public TestTaskInfo(String id, String props) {
super();
this.id = id;
this.props = props;
}
}
如果对这样的字段直接序列化,效果就是这样:
java
TestTaskInfo info= new TestTaskInfo("0000", "{\"task_name\":\"RESET\",\"target_id\":22}");
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(info);
System.out.println(json);
输出
json
{"id":"0000","props":"{\"task_name\":\"RESET\",\"target_id\":22}"}
这个结果props
字段被序列化为字符串,如果送给前端并不友好,前端还需要再对props
字段进行解析才能得到里面的值。
@JsonRawValue
jackson的注解com.fasterxml.jackson.annotation.JsonRawValue
可以解决上面的问题,它指示应按原样包含属性的文字字符串值来序列化字段。
所以给props
字段增加@JsonRawValue
就可以在序列化时将它不加双引号号,直接输出一个JSON对象而非字符串
java
@Data
public class TestTaskInfo {
private String id;
@JsonRawValue
private String props;
public TestTaskInfo() {
}
public TestTaskInfo(String id, String props) {
super();
this.id = id;
this.props = props;
}
}
效果是这样:
java
TestTaskInfo info= new TestTaskInfo("0000", "{\"task_name\":\"RESET\",\"target_id\":22}");
ObjectMapper mapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT);
String json = mapper.writeValueAsString(info);
System.out.println(json);
输出:
json
{
"id" : "0000",
"props" : {"task_name":"RESET","target_id":22}
}
RawJsonDeserializer
解决了JSON字符串(String类型)字段序列化的问题,那么反序列化的问题又来了。
json
{
"id" : "0000",
"props" : {"task_name":"RESET","target_id":22}
}
上面的输出在反序列化为TestTaskInfo
对象时就会出问题,因为props
的类型是String
,但这个JSON中props是个OBJECT
,所以在反序列化时会报错
com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.lang.String out of START_OBJECT token
要解决反序列化的问题,就得实现自定义的反序列化器。不论输入的JSON类型是什么,直接将内容段反序列化为String。这样就与props字段的类型匹配了。
以下是自定义反序列化器的实现:
java
import java.io.IOException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* Deserializing JSON property as String with Jackson<br>
* 实现将有{@link com.fasterxml.jackson.annotation.JsonRawValue}注解的
* 内容为JSON的String类型字段反序列化为String的反序列化器实现
*
*/
public class RawJsonDeserializer extends JsonDeserializer<String> {
@Override
public String deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
ObjectMapper mapper = (ObjectMapper) jp.getCodec();
JsonNode node = mapper.readTree(jp);
/** 将节点树输出为String */
return mapper.writeValueAsString(node);
}
}
我们再修改TestTaskInfo
类型定义为props
字段增加注解定义反序列化器:
java
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import gu.sql2java.json.RawJsonDeserializer;
import lombok.Data;
@Data
public class TestTaskInfo {
private String id;
@JsonRawValue
@JsonDeserialize(using = RawJsonDeserializer.class)
private String props;
public TestTaskInfo() {
}
public TestTaskInfo(String id, String props) {
super();
this.id = id;
this.props = props;
}
}
再次测试:
java
import static org.junit.Assert.*;
import org.junit.Test;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
public class RawJsonDeserializerTest {
@Test
public void testJacksonCodec() {
try {
TestTaskInfo info= new TestTaskInfo("0000", "{\"task_name\":\"RESET\",\"target_id\":22}");
ObjectMapper mapper = new ObjectMapper()/* .enable(SerializationFeature.INDENT_OUTPUT) */;
/** 序列化 */
String json = mapper.writeValueAsString(info);
System.out.println(json);
/** 反序列化 */
TestTaskInfo parsed = mapper.readValue(json, TestTaskInfo.class);
System.out.println(parsed.toString());
assertTrue(parsed.equals(info));
} catch (Throwable e) {
e.printStackTrace();
fail();
}
}
}
这样就可以正常反序列化了,
输出
{"id":"0000","props":{"task_name":"RESET","target_id":22}}
TestTaskInfo(id=0000, props={"task_name":"RESET","target_id":22})