英文:
How deserialize json object array as array of json strings?
问题
考虑 JSON 输入:
{
"companies": [
{
"id": 1,
"name": "name1"
},
{
"id": 1,
"name": "name1"
}
],
"nextPage": 2
}
如何将其反序列化为以下类:
public class MyClass {
List<String> companies;
Integer nextPage;
}
其中 List<String> companies;
包含以下字符串:
{"id": 1, "name": "name1"}
{"id": 1, "name": "name1"}
@JsonRawValue
对于 List<String> companies;
不起作用。
是否有一种方法可以配置 Jackson 序列化,只使用注解将 companies 数组保留为原始 JSON 字符串?(例如,不编写自定义反序列化器)
英文:
Consider json input:
{
companies: [
{
"id": 1,
"name": "name1"
},
{
"id": 1,
"name": "name1"
}
],
nextPage: 2
}
How deserialize this into class:
public class MyClass {
List<String> companies;
Integer nextPage;
}
Where List<String> companies;
consists of strings:
{"id": 1,"name": "name1"}
{"id": 1,"name": "name1"}
@JsonRawValue
doesn't work for List<String> companies;
Is there a way to configure Jackson serialization to keep companies array with raw json string with annotations only? (E.g. without writing custom deserializator)
答案1
得分: 0
以下是翻译好的内容:
没有仅使用注解的解决方案来解决您的问题。不过您需要将 JSON
对象转换为 java.lang.String
,并且需要指定该转换。
您可以:
- 编写自定义反序列化器,这可能是最明显的解决方案,但在问题中被禁止。
- 注册自定义的
com.fasterxml.jackson.databind.deser.DeserializationProblemHandler
,并以更复杂的方式处理com.fasterxml.jackson.databind.exc.MismatchedInputException
的情况。 - 实现
com.fasterxml.jackson.databind.util.Converter
接口,将JsonNode
转换为String
。这是一种半注解化的解决问题的方式,但我们没有实现最糟糕的部分 - 反序列化。
现在来看看第二种解决方案。
- DeserializationProblemHandler
解决方案相当简单:
ObjectMapper mapper = new ObjectMapper();
mapper.addHandler(new DeserializationProblemHandler() {
@Override
public Object handleUnexpectedToken(DeserializationContext ctxt, JavaType targetType, JsonToken t, JsonParser p, String failureMsg) throws IOException {
if (targetType.getRawClass() == String.class) {
// 以树状结构读取并转换为字符串
return p.readValueAsTree().toString();
}
return super.handleUnexpectedToken(ctxt, targetType, t, p, failureMsg);
}
});
将整个 JSON
作为 TreeNode
读取,并使用 toString
方法将其转换为 String
。幸运的是,toString
生成有效的 JSON
。不足之处是,该解决方案对于给定的 ObjectMapper
实例具有全局范围。
- 自定义转换器
此解决方案需要实现 com.fasterxml.jackson.databind.util.Converter
接口,将 com.fasterxml.jackson.databind.JsonNode
转换为 String
:
class JsonNode2StringConverter implements Converter<JsonNode, String> {
@Override
public String convert(JsonNode value) {
return value.toString();
}
@Override
public JavaType getInputType(TypeFactory typeFactory) {
return typeFactory.constructType(new TypeReference<JsonNode>() {
});
}
@Override
public JavaType getOutputType(TypeFactory typeFactory) {
return typeFactory.constructType(new TypeReference<String>() {
});
}
}
现在,您可以像下面这样使用注解:
@JsonDeserialize(contentConverter = JsonNode2StringConverter.class)
private List<String> companies;
解决方案 2 和 3 几乎以相同的方式解决了这个问题 - 读取节点并将其转换回 JSON
,但使用了不同的方法。
如果您想避免反序列化和序列化过程,您可以查看本文中提供的解决方案:使用Jackson将JSON属性反序列化为String,并参考以下内容:
英文:
There is no annotation-only solution for your problem. Somehow you have to convert JSON Object
to java.lang.String
and you need to specify that conversion.
You can:
- Write custom deserializer which is probably most obvious solution but forbidden in question.
- Register custom
com.fasterxml.jackson.databind.deser.DeserializationProblemHandler
and handlecom.fasterxml.jackson.databind.exc.MismatchedInputException
situation in more sophisticated way. - Implement
com.fasterxml.jackson.databind.util.Converter
interface and convertJsonNode
toString
. It is semi-annotational way to solve a problem but we do not implement the worst part - deserialisation.
Let's go to point 2. right away.
- DeserializationProblemHandler
Solution is pretty simple:
ObjectMapper mapper = new ObjectMapper();
mapper.addHandler(new DeserializationProblemHandler() {
@Override
public Object handleUnexpectedToken(DeserializationContext ctxt, JavaType targetType, JsonToken t, JsonParser p, String failureMsg) throws IOException {
if (targetType.getRawClass() == String.class) {
// read as tree and convert to String
return p.readValueAsTree().toString();
}
return super.handleUnexpectedToken(ctxt, targetType, t, p, failureMsg);
}
});
Read a whole piece of JSON
as TreeNode
and convert it to String
using toString
method. Helpfully, toString
generates valid JSON
. Downside, this solution has a global scope for given ObjectMapper
instance.
- Custom Converter
This solution requires to implement com.fasterxml.jackson.databind.util.Converter
interface which converts com.fasterxml.jackson.databind.JsonNode
to String
:
class JsonNode2StringConverter implements Converter<JsonNode, String> {
@Override
public String convert(JsonNode value) {
return value.toString();
}
@Override
public JavaType getInputType(TypeFactory typeFactory) {
return typeFactory.constructType(new TypeReference<JsonNode>() {
});
}
@Override
public JavaType getOutputType(TypeFactory typeFactory) {
return typeFactory.constructType(new TypeReference<String>() {
});
}
}
and now, you can use annotation like below:
@JsonDeserialize(contentConverter = JsonNode2StringConverter.class)
private List<String> companies;
Solutions 2. and 3. solve this problem almost in the same way - read node and convert it back to JSON
, but uses different approaches.
If, you want to avoid deserialising and serialising process you can take a look on solution provided in this article: Deserializing JSON property as String with Jackson and take a look at:
专注分享java语言的经验与见解,让所有开发者获益!
评论