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》

相关推荐
Mephisto.java31 分钟前
【大数据学习 | kafka高级部分】kafka的kraft集群
大数据·sql·oracle·kafka·json·hbase
Mephisto.java33 分钟前
【大数据学习 | kafka高级部分】kafka的文件存储原理
大数据·sql·oracle·kafka·json
待磨的钝刨4 小时前
【格式化查看JSON文件】coco的json文件内容都在一行如何按照json格式查看
开发语言·javascript·json
小百菜18 小时前
dom4j实现xml转map,xml转json字符串
xml·json·xml转map·xml转json
yuchangchenTT20 小时前
就是这个样的粗爆,手搓一个计算器:JSON格式化计算器
前端·json·365快速计算器·在线计算器
engchina20 小时前
Python代码解析:处理JSON数据并导入Neo4j数据库
数据库·python·json
Mephisto.java1 天前
【大数据学习 | HBASE】hbase的整体架构
大数据·sql·oracle·json·hbase·database
东方巴黎~Sunsiny1 天前
优雅的遍历JSONArray,获取里面的数据
java·json
API快乐传递者2 天前
用 Python 爬取淘宝商品价格信息时需要注意什么?
java·开发语言·爬虫·python·json
Mephisto.java2 天前
【大数据学习 | kafka】kafka的偏移量管理
大数据·sql·oracle·sqlite·json·hbase