自动将字符串反序列化为对象,使用Jackson。

huangapple go评论76阅读模式
英文:

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&lt;Custom&gt; 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&lt;Dto&gt; 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:

[
{
&quot;name&quot;: &quot;Bob&quot;,
&quot;customs&quot;: [
&quot;{\n        \&quot;something\&quot;: \&quot;yes\&quot;,\n        \&quot;else\&quot;: \&quot;no\&quot;\n      }&quot;
]
}
]

Notice how the List&lt;Custom&gt; actually ends up being received as a List&lt;String&gt;. 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&lt;Custom&gt; 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&lt;String&gt; 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&lt;String, Custom&gt; {
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&lt;Custom&gt; 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&lt;Custom&gt; { 
 
    public CustomDeserializer() { 
        this(null); 
    } 
 
    public CustomDeserializer(Class&lt;?&gt; 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(&quot;id&quot;)).numberValue();
        String name = node.get(&quot;name&quot;).asText();
        ...
        return new Custom(id, name, ...);
    }
}

and register the deserializer on the Custom class:

@JsonDeserialize(using = CustomDeserializer.class)
public class Custom {
  ...
}

huangapple
  • 本文由 发表于 2020年9月15日 04:40:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/63891548.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定