Je souhaite utiliser le désérialiseur JSON personnalisé pour certaines classes ( Role here) mais je ne parviens pas à le faire fonctionner Le désérialiseur personnalisé n'est tout simplement pas appelé.
J'utilise Spring Boot 1.2.
Désérialiseur:
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
}
}
Manette:
@RestController
public class RoleController {
@RequestMapping(value = "/role", method = RequestMethod.POST)
public Object createRole(Role role) {
// ... this is called
}
}
@JsonDeserialize
sur le rôle
@JsonDeserialize(using = ModelDeserializer.class)
public class Role extends Model {
}
Jackson2ObjectMapperBuilder
bean dans Java Config
@Bean
public Jackson2ObjectMapperBuilder jacksonBuilder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.deserializerByType(Role.class, new ModelDeserializer());
return builder;
}
Qu'est-ce que je fais mal?
EDITCela est probablement dû à @RestController
car cela fonctionne avec @Controller
...
Tout d'abord, vous n'avez pas besoin de remplacer Jackson2ObjectMapperBuilder
pour ajouter un désérialiseur personnalisé. Cette approche doit être utilisée lorsque vous ne pouvez pas ajouter d'annotation @JsonDeserialize
. Vous devez utiliser @JsonDeserialize
ou remplacer Jackson2ObjectMapperBuilder
.
Ce qui manque, c'est l'annotation @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;
}
}
Il existe également une autre solution intéressante qui peut s'avérer utile si vous souhaitez modifier votre corps JSON avant d'appeler le désérialiseur par défaut. Et imaginons que vous deviez utiliser un bean supplémentaire pour cela (utilisez le mécanisme @Autowire
)
Imaginons que vous ayez le contrôleur suivant:
@RequestMapping(value = "/order/product", method = POST)
public <T extends OrderProductInterface> RestGenericResponse orderProduct(@RequestBody @Valid T data) {
orderService.orderProduct(data);
return generateResponse();
}
Où OrderProductInterface
est:
@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{}
Le code ci-dessus fournira une base de désérialisation dynamique sur la variable providerType
archivée et une validation en fonction de la mise en œuvre concrète. Pour une meilleure compréhension, considérez que OrderProductForARequestData
peut être quelque chose comme ça:
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;
}
Et imaginons maintenant que nous voulons lancer providerType
(enrichir l'entrée) avant que la désérialisation par défaut ne soit exécutée. donc l'objet sera désérialisé correctement conformément à la règle dans OrderProductInterface
. Pour cela, vous pouvez simplement modifier votre classe @Configuration
de la manière suivante:
//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);
}
}
}