Embed object instead of collection in Spring HATEOAS

一世执手 提交于 2019-12-24 00:12:46

问题


A very quick question ,to which there seems to be no easy answer.

Is it possible to put an object directly under the embedded resources using Spring HATEOAS? The desired output format in JSON should look like

{
    ...
    _embedded: {
        myObject: {
            ...
        }
    }
}

Using the code below, I always end up with a colletion for any resource I want to embed.

ArrayList<Resource<?>> embeddedContent = new ArrayList<>();
Resource<MyObject> myObjectResource = new Resource<MyObject>(new MyObject());
embeddedContent.add(myObjectResource );
Resources<Resource<?>> embeddedResources = new Resources<Resource<?>>(embeddedContent);

The embeddedResources are then put on a class, which is later mapped to a resource as well.

But for some reason, even though I'm not adding a collection to the embedded resources, the output still shows the myObject embedded resource as an array:

{
    ...
    _embedded: {
        myObject: [
            {
                ...
            }
        ]
    }
}

回答1:


The parameter enforceEmbeddedCollections in this constructor allow represent embedded arrays like a object.

 public HalHandlerInstantiator(RelProvider resolver, CurieProvider curieProvider, boolean enforceEmbeddedCollections) {}        

So, you should set HalHandlerInstantiator with value false. There is a small example:

    ObjectMapper halObjectMapper = new ObjectMapper();
    halObjectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    halObjectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);

    halObjectMapper
            .setHandlerInstantiator(new Jackson2HalModule.
                    HalHandlerInstantiator(new DefaultRelProvider(), null, false));

    Jackson2HalModule jackson2HalModule = new Jackson2HalModule();

    halObjectMapper.registerModule(jackson2HalModule);
    try {
        halObjectMapper.writeValueAsString(new Resources<Album>(Arrays.asList(new Album("1", "title", "1", 1))));
    } catch (JsonProcessingException e) {
        e.printStackTrace();
    }



回答2:


Add the following snippet to one of your @Configuration classes. The code here looks similar to what can be found in org.springframework.hateoas.config.HypermediaSupportBeanDefinitionRegistrar. We're basically overwriting the HalHandlerInstantiator in the HAL-ObjectMapper where we pass false to the enforceEmbeddedCollections argument. This is a dirty hack, but currently there's no way to configure this aspect of the spring-hateoas machinery.

@Bean
BeanPostProcessor halModuleReconfigurer(BeanFactory beanFactory) {
    return new BeanPostProcessor() {
        @Override
        public Object postProcessBeforeInitialization(final Object bean, final String beanName) throws BeansException {
            return bean;
        }

        @Override
        public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException {
            if (bean instanceof RequestMappingHandlerAdapter) {

                RequestMappingHandlerAdapter adapter = (RequestMappingHandlerAdapter) bean;
                adapter.setMessageConverters(reconfigureObjectMapper(adapter.getMessageConverters()));
            }

            if (bean instanceof AnnotationMethodHandlerAdapter) {

                AnnotationMethodHandlerAdapter adapter = (AnnotationMethodHandlerAdapter) bean;
                List<HttpMessageConverter<?>> augmentedConverters = reconfigureObjectMapper(Arrays.asList(adapter
                    .getMessageConverters()));
                adapter
                    .setMessageConverters(augmentedConverters.toArray(new HttpMessageConverter<?>[augmentedConverters.size()]));
            }

            if (bean instanceof RestTemplate) {

                RestTemplate template = (RestTemplate) bean;
                template.setMessageConverters(reconfigureObjectMapper(template.getMessageConverters()));
            }

            return bean;
        }

        private List<HttpMessageConverter<?>> reconfigureObjectMapper(final List<HttpMessageConverter<?>> converters) {
            for (HttpMessageConverter<?> converter : converters) {
                if (converter instanceof MappingJackson2HttpMessageConverter) {
                    MappingJackson2HttpMessageConverter halConverterCandidate = (MappingJackson2HttpMessageConverter) converter;
                    ObjectMapper objectMapper = halConverterCandidate.getObjectMapper();
                    if (Jackson2HalModule.isAlreadyRegisteredIn(objectMapper)) {
                        final CurieProvider curieProvider = Try.of(() -> beanFactory.getBean(CurieProvider.class)).getOrElse((CurieProvider) null);
                        final RelProvider relProvider = beanFactory.getBean("_relProvider", RelProvider.class);
                        final MessageSourceAccessor linkRelationMessageSource = beanFactory.getBean("linkRelationMessageSource", MessageSourceAccessor.class);
                        objectMapper.setHandlerInstantiator(new Jackson2HalModule.HalHandlerInstantiator(relProvider, curieProvider, linkRelationMessageSource, false));
                    }
                }
            }

            return converters;
        }
    };
}


来源:https://stackoverflow.com/questions/33777198/embed-object-instead-of-collection-in-spring-hateoas

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!