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》

相关推荐
敬业小码哥17 小时前
记一次:mysql的json及json数组使用组合使用
数据库·mysql·json
一键三联啊17 小时前
BSON 和 JSON 的区别
json
一个很老的小萌新2 天前
json 解析 [{“id“:1,“name“:“apple“},{“id“:2,“name“:“banana“}]
java·前端·json
微笑伴你而行3 天前
目标检测如何将同时有方形框和旋转框的json/xml标注转为txt格式
xml·目标检测·json
cdcdhj3 天前
数据库存储大量的json文件怎么样高效的读取和分页,利用文件缓存办法不占用内存
缓存·node.js·json
Z_z在努力3 天前
【杂类】JSON:现代Web开发的数据交换基石
json
上官浩仁4 天前
springboot jackson json入门与实战
java·spring boot·json
GISBox4 天前
GISBox矢量服务使用指南:从数据导入到服务发布
react.js·json·gis
Dxy12393102164 天前
Python如何处理非标准JSON
开发语言·python·json
IAM四十二5 天前
Android Json 解析你还在用 fastjson 吗?
android·json·fastjson