Spring @RestController настраиваемый десериализатор JSON
Я хочу использовать пользовательский десериализатор JSON для некоторых классов ( Роль здесь), но я не могу заставить его работать. Пользовательский десериализатор просто не вызывается.
Я использую Spring Boot 1.2.
десериализатор:
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
}
}
Контроллер:
@RestController
public class RoleController {
@RequestMapping(value = "/role", method = RequestMethod.POST)
public Object createRole(Role role) {
// ... this is called
}
}
-
@JsonDeserialize
о роли
@JsonDeserialize(using = ModelDeserializer.class)
public class Role extends Model {
}
-
Jackson2ObjectMapperBuilder
bean в конфигурации Java
@Bean
public Jackson2ObjectMapperBuilder jacksonBuilder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.deserializerByType(Role.class, new ModelDeserializer());
return builder;
}
Что я делаю неправильно?
EDIT Вероятно, это вызвано @RestController
, поскольку оно работает с @Controller
...
Ответы
Ответ 1
Прежде всего вам не нужно переопределять Jackson2ObjectMapperBuilder
, чтобы добавить пользовательский десериализатор. Этот подход следует использовать, если вы не можете добавить аннотацию @JsonDeserialize
. Вы должны использовать @JsonDeserialize
или переопределить Jackson2ObjectMapperBuilder
.
Пропущено аннотация @RequestBody
:
@RestController
public class JacksonCustomDesRestEndpoint {
@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;
}
}
Ответ 2
Существует еще одно довольно интересное решение, которое может быть полезно в случае, если вы хотите изменить свой корпус JSON, прежде чем вызывать десериализатор по умолчанию. И представьте себе, что вам нужно использовать для этого несколько дополнительных bean (используйте механизм @Autowire
)
Пусть ситуация с изображением, что у вас есть следующий контроллер:
@RequestMapping(value = "/order/product", method = POST)
public <T extends OrderProductInterface> RestGenericResponse orderProduct(@RequestBody @Valid T data) {
orderService.orderProduct(data);
return generateResponse();
}
Где 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{}
Приведенный выше код предоставит динамическую базу десериализации на поданной 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;
}
И пусть теперь изображение, которое мы хотим как-то инициализировать 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 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);
}
}
}