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

文章讲述了如何在Java中使用Jackson库处理`props`字段,该字段通常保存JSON字符串。通过添加`@JsonRawValue`注解,序列化时不加双引号并保持原始JSON结构。同时,为了解决反序列化时类型不匹配的问题,创建了一个自定义的`RawJsonDeserializer`,确保了JSON内容正确地反序列化为`String`。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

对于如下类型定义TestTaskInfoprops字段为JSON字符串(这在数据库经常用到),可以自由保存各种类型的数据

@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;
	}
}

如果对这样的字段直接序列化,效果就是这样:

TestTaskInfo info=  new TestTaskInfo("0000", "{\"task_name\":\"RESET\",\"target_id\":22}");
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(info);
System.out.println(json);

输出

{"id":"0000","props":"{\"task_name\":\"RESET\",\"target_id\":22}"}

这个结果props字段被序列化为字符串,如果送给前端并不友好,前端还需要再对props字段进行解析才能得到里面的值。

@JsonRawValue

jackson的注解com.fasterxml.jackson.annotation.JsonRawValue可以解决上面的问题,它指示应按原样包含属性的文字字符串值来序列化字段。
所以给props字段增加@JsonRawValue就可以在序列化时将它不加双引号号,直接输出一个JSON对象而非字符串

@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;
	}
}

效果是这样:

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);

输出:

{
  "id" : "0000",
  "props" : {"task_name":"RESET","target_id":22}
}

RawJsonDeserializer

解决了JSON字符串(String类型)字段序列化的问题,那么反序列化的问题又来了。

{
  "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字段的类型匹配了。

以下是自定义反序列化器的实现:


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字段增加注解定义反序列化器:

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;
	}
}

再次测试:

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》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

10km

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值