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》

相关推荐
nbsaas-boot10 小时前
探索 JSON 数据在关系型数据库中的应用:MySQL 与 SQL Server 的对比
数据库·mysql·json
疯一样的码农12 小时前
Jackson 的@JsonRawValue
json
Web打印15 小时前
web打印插件 HttpPrinter 使用半年评测
javascript·json·firefox·jquery·html5
手心里的白日梦16 小时前
网络计算器的实现:TCP、守护进程、Json、序列化与反序列化
网络·tcp/ip·json
chenchihwen16 小时前
数据分析时的json to excel 转换的好用小工具
数据分析·json·excel
子燕若水18 小时前
简要解释JSON Schema
前端·html·json
Json_1817901448019 小时前
淘系商品评论json数据示例参考,API接口系列
大数据·json·api
慕羽★1 天前
详细介绍如何使用rapidjson读取json文件
linux·c++·windows·json·file·param·rapidjson
轻口味1 天前
配置TypeScript:tsconfig.json详解
ubuntu·typescript·json
zybishe1 天前
免费送源码:Java+ssm++MVC+HTML+CSS+MySQL springboot 社区医院信息管理系统的设计与实现 计算机毕业设计原创定制
java·hadoop·sql·zookeeper·html·json·mvc