WebFlux: как десериализовать параметр интерфейса контроллера?

#jackson #spring-webflux

#джексон #spring-webflux

Вопрос:

Я использую Spring Webflux, и мне нужно десериализовать объект интерфейса, переданный в качестве параметра a Restcontroller .

Пожалуйста, обратите внимание, что мне не разрешено каким-либо образом редактировать интерфейс, поскольку он из другой библиотеки. Я использую Lombok здесь, чтобы уменьшить шаблонный код.

Это интерфейс

 public interface MyInterface {
    String getField();
    void setField(String field);
}
 

с соответствующей реализацией

 @Data
@AllArgsConstructor
@NoArgsConstructor
public class MyInterfaceImpl implements MyInterface {
    private String field;
}
 

и контроллер Rest

 @RestController
public class TestController {
    @PostMapping("/interface/test")
    public String test(@RequestBody MyInterface myInterface){
        return myInterface.toString();
    }
}
 

В этом примере я использую этот JSON для MyInterface:

 {
    "field":"Test interface field"
}
 

Теперь, если я попытаюсь вызвать конечную точку, как она есть, с помощью приведенного выше JSON,
Я получаю следующую ошибку:

 org.springframework.core.codec.CodecException: Type definition error: [simple type, class project.dto.model.MyInterface]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `project.dto.model.MyInterface` (no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
 at [Source: (io.netty.buffer.ByteBufInputStream); line: 1, column: 1]
 

Что я сделал, так это внедрил пользовательский JsonDeserializer

 public class MyInterfaceDeserializer extends JsonDeserializer<MyInterface> {
    @Override
    public MyInterface deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
        return jsonParser.readValueAs(MyInterfaceImpl.class);
    }
}
 

и предоставить его в виде модуля:

 @Configuration
public class Jacksonconfiguration {

    @Bean
    Module myInterfaceDeserializerModule(){
        SimpleModule module = new SimpleModule();
        module.addDeserializer(MyInterface.class, new MyInterfaceDeserializer());
        return module;
    }
}
 

Однако модуль, похоже, не регистрируется автоматически, как задокументировано, и ошибка десериализации все еще остается.
Как я могу сделать так, чтобы MyInterface автоматически десериализовался из параметра контроллера?

Ответ №1:

Если у вас есть предварительно настроенный ObjectMapper, вы можете использовать CommandLineRunner для добавления модуля:

 @Configuration
public class JacksonConfiguration implements CommandLineRunner {
    
    private final ObjectMapper objectMapper;
    
    public JacksonConfiguration(final ObjectMapper objectMapper ) {
            this.objectMapper = objectMapper;
    }
    
    @Override
    public void run(final String... args ) {
        SimpleModule module = new SimpleModule();
        module.addDeserializer( MyInterface.class, new MyInterfaceDeserializer() );
        objectMapper.registerModule( module );
    }
}