在Spring HATEOAS中嵌入对象而不是集合

时间:2015-11-18 10:12:34

标签: spring hateoas spring-hateoas hal

一个非常快速的问题,似乎没有简单的答案。

是否可以使用Spring HATEOAS将对象直接放在嵌入式资源下? JSON中所需的输出格式应该类似于

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

使用下面的代码,我总是会收集我想要嵌入的任何资源。

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

然后将embeddedResources放在一个类上,该类稍后也会映射到一个资源。

但由于某些原因,即使我没有向嵌入式资源添加集合,输出仍然将myObject嵌入资源显示为数组:

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

2 个答案:

答案 0 :(得分:1)

此构造函数中的参数 enforceEmbeddedCollections 允许表示嵌入式数组,如对象。

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

因此,您应该将HalHandlerInstantiator设置为值false。 有一个小例子:

    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();
    }

答案 1 :(得分:1)

将以下代码段添加到您的@Configuration个类之一。此处的代码与org.springframework.hateoas.config.HypermediaSupportBeanDefinitionRegistrar中的代码类似。我们基本上覆盖了HAL-ObjectMapper中的HalHandlerInstantiator,我们将false传递给enforceEmbeddedCollections参数。这是一个肮脏的黑客,但目前还没有办法配置spring-hateoas机器的这个方面。

@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;
        }
    };
}