2014-12-18 7 views
12

Я хочу использовать собственный десериализатор JSON для некоторых классов (Роль здесь), но я не могу заставить его работать. Пользовательский десериализатор просто не вызывается.Spring @RestController custom JSON deserializer

Я использую 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 
    } 
} 
  1. @JsonDeserialize о роли

    @JsonDeserialize(using = ModelDeserializer.class) 
    public class Role extends Model { 
    
    } 
    
  2. Jackson2ObjectMapperBuilder фасоль в Java Config

    @Bean 
    public Jackson2ObjectMapperBuilder jacksonBuilder() { 
        Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder(); 
        builder.deserializerByType(Role.class, new ModelDeserializer()); 
        return builder; 
    } 
    

Что я делаю неправильно?

EDIT Это, вероятно, вызвано @RestController, потому что он работает с @Controller ...

ответ

19

Прежде всего, вам не нужно переопределить 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; 
    } 
} 
+0

мне нужно сделать то же самое, с той лишь разницей, что у меня есть метод получить и в моем объекте Pojo я использую тип Date с @JsonDeserialize, но когда я запускаю метод, я получаю http 400. (весна 4.1.7 y jackson 2.7.4) – Hector

0

Существует также еще довольно интересное решение, которое может быть полезным в случае, если вы хотите изменить JSON тело перед вызовом по умолчанию десериализации. А давайте представим, что вам нужно использовать некоторый дополнительный боб для этого (использования @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'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); 

      } 

     } 
}