英文:
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.DeserializationProblemHandlerand handlecom.fasterxml.jackson.databind.exc.MismatchedInputExceptionsituation in more sophisticated way. - Implement
com.fasterxml.jackson.databind.util.Converterinterface and convertJsonNodetoString. 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语言的经验与见解,让所有开发者获益!

评论