Java Spring @RestController 自定义 JSON 反序列化器
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/27550376/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Spring @RestController custom JSON deserializer
提问by jakub.petr
I want to use custom JSON deserializer for some classes(Rolehere) but I can't get it working. The custom deserializer just isn't called.
我想对某些类使用自定义 JSON 反序列化器(此处为角色),但无法正常工作。只是没有调用自定义解串器。
I use Spring Boot 1.2.
我使用 Spring Boot 1.2。
Deserializer:
解串器:
public class ModelDeserializer extends JsonDeserializer<Role> {
@Override
public Role deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
return null; // this is what should be called but it isn't
}
}
Controller:
控制器:
@RestController
public class RoleController {
@RequestMapping(value = "/role", method = RequestMethod.POST)
public Object createRole(Role role) {
// ... this is called
}
}
@JsonDeserialize
on Role@JsonDeserialize(using = ModelDeserializer.class) public class Role extends Model { }
Hymanson2ObjectMapperBuilder
bean in Java Config@Bean public Hymanson2ObjectMapperBuilder HymansonBuilder() { Hymanson2ObjectMapperBuilder builder = new Hymanson2ObjectMapperBuilder(); builder.deserializerByType(Role.class, new ModelDeserializer()); return builder; }
@JsonDeserialize
关于角色@JsonDeserialize(using = ModelDeserializer.class) public class Role extends Model { }
Hymanson2ObjectMapperBuilder
Java 配置中的 bean@Bean public Hymanson2ObjectMapperBuilder HymansonBuilder() { Hymanson2ObjectMapperBuilder builder = new Hymanson2ObjectMapperBuilder(); builder.deserializerByType(Role.class, new ModelDeserializer()); return builder; }
What am I doing wrong?
我究竟做错了什么?
EDITIt is probably caused by @RestController
because it works with @Controller
...
编辑这可能是@RestController
因为它适用于@Controller
...
采纳答案by Ilya Ovesnov
First of all you don't need to override Hymanson2ObjectMapperBuilder
to add custom deserializer. This approach should be used when you can't add @JsonDeserialize
annotation. You should use @JsonDeserialize
or override Hymanson2ObjectMapperBuilder
.
首先,您无需覆盖Hymanson2ObjectMapperBuilder
即可添加自定义反序列化器。当您无法添加@JsonDeserialize
注释时,应使用此方法。您应该使用@JsonDeserialize
或覆盖Hymanson2ObjectMapperBuilder
.
What is missed is the @RequestBody
annotation:
遗漏的是@RequestBody
注释:
@RestController
public class HymansonCustomDesRestEndpoint {
@RequestMapping(value = "/role", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public Object createRole(@RequestBody Role role) {
return role;
}
}
@JsonDeserialize(using = RoleDeserializer.class)
public class Role {
// ......
}
public class RoleDeserializer extends JsonDeserializer<Role> {
@Override
public Role deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
// .................
return something;
}
}
回答by povisenko
There is also another pretty interesting solution which can be helpful in case when you want to modify your JSON body before calling default deserializer. And let's imagine that you need to use some additional bean for that (use @Autowire
mechanism)
还有另一个非常有趣的解决方案,如果您想在调用默认反序列化器之前修改 JSON 主体,它会很有帮助。让我们想象一下,您需要为此使用一些额外的 bean(使用@Autowire
机制)
Let's image situation, that you have the following controller:
让我们想象一下,您有以下控制器:
@RequestMapping(value = "/order/product", method = POST)
public <T extends OrderProductInterface> RestGenericResponse orderProduct(@RequestBody @Valid T data) {
orderService.orderProduct(data);
return generateResponse();
}
Where OrderProductInterface
is:
在哪里OrderProductInterface
:
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonSerialize(include = NON_EMPTY)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, visible = true, property = "providerType")
@JsonSubTypes({
@JsonSubTypes.Type(value = OrderProductForARequestData.class, name = "A")
})
public interface OrderProductInterface{}
The code above will provide dynamic deserialization base on filed providerType
and validation according to concrete implementation. For better grasp, consider that OrderProductForARequestData
can be something like that:
上面的代码将providerType
根据具体实现提供基于归档和验证的动态反序列化。为了更好地掌握,请考虑以下内容OrderProductForARequestData
:
public class OrderProductForARequestData implements OrderProductInterface {
@NotBlank(message = "is mandatory field.")
@Getter @Setter
private String providerId;
@NotBlank(message = "is mandatory field.")
@Getter @Setter
private String providerType;
@NotBlank(message = "is mandatory field.")
@Getter @Setter
private String productToOrder;
}
And let's image now that we want to init somehow providerType
(enrich input) before default deserialization will be executed.so the object will be deserialized properly according to the rule in OrderProductInterface
.
To do that you can just modify your @Configuration
class in the following way:
现在让我们想象一下,我们想在执行默认反序列化之前以某种方式初始化providerType
(丰富输入)。因此该对象将根据 中的规则正确反序列化OrderProductInterface
。为此,您可以@Configuration
通过以下方式修改您的类:
//here can be any annotation which will enable MVC/Boot
@Configuration
public class YourConfiguration{
@Autowired
private ObjectMapper mapper;
@Autowired
private ProviderService providerService;
@Override
public void setup() {
super.setup();
SimpleModule module = new SimpleModule();
module.setDeserializerModifier(new BeanDeserializerModifier() {
@Override
public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
if (beanDesc.getBeanClass() == OrderProductInterface.class) {
return new OrderProductInterfaceDeserializer(providerService, beanDesc);
}
return deserializer;
}
});
mapper.registerModule(module);
}
public static class OrderProductInterfaceDeserializer extends AbstractDeserializer {
private static final long serialVersionUID = 7923585097068641765L;
private final ProviderService providerService;
OrderProductInterfaceDeserializer(roviderService providerService, BeanDescription beanDescription) {
super(beanDescription);
this.providerService = providerService;
}
@Override
public Object deserializeWithType(JsonParser p, DeserializationContext context, TypeDeserializer typeDeserializer) throws IOException {
ObjectCodec oc = p.getCodec();
JsonNode node = oc.readTree(p);
//Let's image that we have some identifier for provider type and we want to detect it
JsonNode tmp = node.get("providerId");
Assert.notNull(tmp, "'providerId' is mandatory field");
String providerId = tmp.textValue();
Assert.hasText(providerId, "'providerId' can't be empty");
// Modify node
((ObjectNode) node).put("providerType",providerService.getProvider(providerId));
JsonFactory jsonFactory = new JsonFactory();
JsonParser newParser = jsonFactory.createParser(node.toString());
newParser.nextToken();
return super.deserializeWithType(newParser, context, typeDeserializer);
}
}
}