jackson:JSON字符串(String)类型的成员序列化和反序列化

对于如下类型定义TestTaskInfoprops字段为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})

参考资料

《Deserializing JSON property as String with Jackson》

相关推荐
疯狂SQL1 天前
手写高性能在线 JSON 工具|Web Worker 工程化打包 + 语法自动修复 + 多语言代码生成实战
typescript·json·next.js·web worker·前端性能优化·esbuild·源码实战
terry6006 天前
5G视频短信服务商选型全攻略:通道资源、架构能力与成本评估2026最新标准
大数据·人工智能·5g·json·asp.net·信息与通信·数据库架构
前网易架构师-高司机6 天前
带标注的辣椒病叶数据集,识别率95.9%,可识别三种病害和健康叶子,9916张图,支持yolo,coco json,voc xml,文末有模型训练代码
yolo·json·数据集·病害·叶病·病叶·辣椒
PixelBai6 天前
JSON扁平化使用教程:从入门到精通
json
渔舟唱晚,雁阵惊寒7 天前
CSDN博客内容丢失如何恢复?
json
衣乌安、7 天前
JSON-RPC协议
网络协议·rpc·json
PixelBai7 天前
JSON过滤使用教程:从入门到精通
javascript·chrome·json
PixelBai7 天前
JSON过滤实际应用场景案例
json
一生了无挂8 天前
Java处理JSON技巧教学(从基础到高阶实战全覆盖)
java·开发语言·json