Пользовательский сериализатор не вызывается для массивов или типов коллекций - PullRequest
1 голос
/ 09 ноября 2019

Я пишу инструмент командной строки с пружинной загрузкой, который должен взаимодействовать с API-интерфейсом, который я уже реализовал. Этот API-интерфейс построен на основе данных Spring с пакетом hateoas, поэтому он генерирует типы сообщений HAL.

В моем инструменте CLI я хочу POST-объект, содержащий список других объектов (отношение один ко многим)). Для более простого использования я хотел использовать типы ресурсов в моделях для выражения отношений и иметь сериализатор JSON для преобразования ресурсов только в свои собственные hrefs.

Мой сериализатор прекрасно работает для отношений один-к-одному, но никогда не получает вызовов для сериализации массивов или любых типов коллекций.

Это то, что API принимает, когда я POST сущность:

{
  "property1": "value1",
  "myrelation" : "http://localhohst:8080/relatedentities/1"
  "mycollection": [
    "http://localhost:8080/otherrelatedentities/2",
    "http://localhost:8080/otherrelatedentities/3"
  ]
}

На стороне CLI я создал модель объекта в приложении CLI следующим образом:

@Getter @Setter
public class MyEntity {
  private String property1;

  @JsonSerialize(using = HateoasResourceIdSerializer.class)
  private Resource<RelatedEnity> myrelation;

  @JsonSerialize(using = HateoasResourceIdSerializer.class)
  private List<Resource<OtherRelatedEntity>> mycollection;
}

Я написал этот HateoasResourceIdSerializer для преобразования любого типа ресурса в только его собственную href:

public class HateoasResourceIdSerializer extends StdSerializer<Resource<?>> {

    private static final long serialVersionUID = 1L;

    public HateoasResourceIdSerializer() {
        this(null);
    }

    public HateoasResourceIdSerializer(Class<Resource<?>> t) {
        super(t);
    }

    @Override
    public void serialize(Resource<?> value, JsonGenerator jgen, SerializerProvider provider)
            throws IOException, JsonProcessingException {
        jgen.writeString(value.getId().getHref());
    }

}

Глядя на полезную нагрузку, отправляемую бэкэнду API, я вижу, что для свойства "myrelation" задан URL-адрес целевой сущности, а свойство "mycollection" всегда равно нулю.

Iпопытался написать 2-й сериализатор, который бы принимал Collection<Resource<?>>, но он тоже не вызывался.

Я ожидал бы, что приведенный выше сериализатор для Resource будет применяться к массивам, а также к любому типу коллекции.

РЕДАКТИРОВАТЬ: Меня попросили предоставить код для регистрации сериализаторов, так что вот оно. Я добавил два миксина, как было предложено в одном из ответов ниже (надеюсь, я все сделал правильно), но не увидел ожидаемого поведения. Я также предположил, что благодаря регистрации я смог удалить аннотацию @JsonSerialize(using = HateoasResource(s)IdSerializer.class) из свойств. В настоящее время эти свойства не отображаются вообще.

@SuppressWarnings("deprecation")
@SpringBootApplication
@EnableHypermediaSupport(type=EnableHypermediaSupport.HypermediaType.HAL)
public class Application extends WebMvcConfigurerAdapter implements ApplicationRunner {

    public static void main(String[] args) {
        SpringApplication.run(SwissArmyKnifeApplication.class, args);
    }

    @Override
    public void run(ApplicationArguments args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        // ...
    }

    @Autowired
    private HalHttpMessageConverter halHttpMessageConverter;

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(halHttpMessageConverter);
        super.configureMessageConverters(converters);
    }

}
@Configuration
public class HalHttpMessageConverter extends AbstractJackson2HttpMessageConverter {

    public HalHttpMessageConverter() {
        super(new ObjectMapper(), new MediaType("application", "hal+json", DEFAULT_CHARSET));
        objectMapper.registerModule(new Jackson2HalModule());
        objectMapper
                .setHandlerInstantiator(new Jackson2HalModule.HalHandlerInstantiator(new DefaultRelProvider(), null, null));
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

        objectMapper.addMixIn(Resource.class, ResourceMixIn.class);
        objectMapper.addMixIn(Resources.class, ResourcesMixIn.class);

    }

    @Override
    protected boolean supports(Class<?> clazz) {
        return ResourceSupport.class.isAssignableFrom(clazz);
    }

}

Ответы [ 2 ]

0 голосов
/ 10 ноября 2019

Вы не включили код для указания того, как вы регистрируете сериализатор для своего типа, чтобы это могло дать подсказку. Но пользовательские сериализаторы определенно должны вызываться для массива, значений Collection и Map, а также простых значений свойств. Регистрация отдельного сериализатора для Collection<Type> не требуется (и на самом деле немного сложнее сделать: возможно, но больше работы из-за вложенного типа) и не предназначена для поддержки только определенного типа в коллекции (а скорее для поддержки более специальногоCollection s, если есть).

Поэтому, пожалуйста, включите код, связанный с регистрацией, а также версию используемого Джексона (и, если не последнюю версию, рассмотрите возможность его обновления в первую очередь, чтобы увидеть, что проблема все еще сохраняется).

0 голосов
/ 09 ноября 2019

Вам необходимо правильно зарегистрировать свой пользовательский сериализатор, используя функцию MixIn. Вместо того, чтобы аннотировать свойство, вам нужно аннотировать класс, который сообщает Jackson, что вы хотите использовать его во всех сценариях, а не только для MyEntity класса.

Создать MixIn интерфейс:

@JsonSerialize(using = HateoasResourceIdSerializer.class)
interface ResourceMixIn {
}

И зарегистрируйте его:

ObjectMapper mapper = JsonMapper.builder()
        .addMixIn(Resource.class, ResourceMixIn.class).build();

Посмотрите другие вопросы, как настроить Jackson mapper в Spring:

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...