英文:
Automatic deserialization of String to Object with Jackson
问题
# Context
假设你有以下代码:
public class Dto {
private String name;
private List<Custom> customs;
// getters and setters...
}
和
public class Custom {
private String something;
private String else;
// getters and setters...
}
你的 Spring MVC ``RestController`` 接收一个 ``Dto`` 的列表:
@PostMapping
public String create(@RequestBody @Valid List<Dto> dtos) {
return myService.process(features);
}
# Input
然而,你知道客户端服务会向你的控制器发送类似这样的数据:
[
{
"name": "Bob",
"customs": [
"{\"something\": \"yes\",\"else\": \"no\"}"
]
}
]
请注意 **``List<Custom>`` 实际上被接收为 ``List<String>``**。请假设在客户端无法更改此情况,我们必须在服务器端处理它。
# Question
**是否有一种 Jackson 注解可以自动将输入的 ``String`` 序列化为 ``Custom`` 类?**
# Attempts
尝试了一些方法,包括:
@JsonSerialize(using = ToStringSerializer.class)
private List<Custom> customs;
以及
public Custom(String json) {
try {
new ObjectMapper().readerFor(Custom.class).readValue(json);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
目前,我们不得不将 ``customs`` 类型更改为 ``List<String>``,并添加一个实用方法,使用 ``ObjectMapper`` 将 ``String`` 转换为 ``Custom``。这相当令人不满。
英文:
Context
Say you have:
public class Dto {
private String name;
private String List<Custom> customs;
// getters and setters...
}
and
public class Custom {
private String something;
private String else;
// getters and setters...
}
Your Spring MVC RestController
receives a list of Dto
:
@PostMapping
public String create(@RequestBody @Valid List<Dto> dtos) {
return myService.process(features);
}
Input
However, you know that the client-side service which will send data to your controller will send something like this:
[
{
"name": "Bob",
"customs": [
"{\n \"something\": \"yes\",\n \"else\": \"no\"\n }"
]
}
]
Notice how the List<Custom>
actually ends up being received as a List<String>
. Please assume this cannot be changed on the client-side and we have to deal with it on the server-side.
Question
Is there a Jackson annotation which would automagically take the input String
and try to serialize it into a Custom
class?
Attempts
A few things that didn't work, including:
@JsonSerialize(using = ToStringSerializer.class)
private List<Custom> customs;
along with
public Custom(String json) {
try {
new ObjectMapper().readerFor(Custom.class).readValue(json);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
As it is, we have had to change the customs
type to List<String>
and add a utility method which converts a String
into a Custom
using an ObjectMapper
. This is rather dissatisfying.
答案1
得分: 1
你需要实现自定义的反序列化器或转换器,用于将给定的数据转换为所需的类型。一个技巧是可以创建一个新的 ObjectMapper
并将其用于内部反序列化。
示例用法:
class CustomConverter extends StdConverter<String, Custom> {
private final ObjectMapper mapper = new ObjectMapper();
@Override
public Custom convert(String value) {
try {
return mapper.readValue(value, Custom.class);
} catch (JsonProcessingException e) {
throw new IllegalArgumentException(value);
}
}
}
class Dto {
private String name;
@JsonDeserialize(contentConverter = CustomConverter.class)
private List<Custom> customs;
}
英文:
You need to implement custom deserialiser or converter which would be used to convert given payload to required type. One trick, you could use is to create new ObjectMapper
and use it for internal deserialisation.
Example usage:
class CustomConverter extends StdConverter<String, Custom> {
private final ObjectMapper mapper = new ObjectMapper();
@Override
public Custom convert(String value) {
try {
return mapper.readValue(value, Custom.class);
} catch (JsonProcessingException e) {
throw new IllegalArgumentException(value);
}
}
}
class Dto {
private String name;
@JsonDeserialize(contentConverter = CustomConverter.class)
private List<Custom> customs;
}
答案2
得分: 0
你需要创建一个自定义的反序列化器。
public class CustomDeserializer extends StdDeserializer<Custom> {
public CustomDeserializer() {
this(null);
}
public CustomDeserializer(Class<?> vc) {
super(vc);
}
@Override
public Custom deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonNode node = jp.getCodec().readTree(jp);
int id = (Integer) ((IntNode) node.get("id")).numberValue();
String name = node.get("name").asText();
...
return new Custom(id, name, ...);
}
}
并在 Custom
类上注册这个反序列化器:
@JsonDeserialize(using = CustomDeserializer.class)
public class Custom {
...
}
英文:
You need to create a custom Deserializer.
public class CustomDeserializer extends StdDeserializer<Custom> {
public CustomDeserializer() {
this(null);
}
public CustomDeserializer(Class<?> vc) {
super(vc);
}
@Override
public Custom deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonNode node = jp.getCodec().readTree(jp);
int id = (Integer) ((IntNode) node.get("id")).numberValue();
String name = node.get("name").asText();
...
return new Custom(id, name, ...);
}
}
and register the deserializer on the Custom
class:
@JsonDeserialize(using = CustomDeserializer.class)
public class Custom {
...
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论