简体   繁体   English

Spring Boot:用于请求和响应的不同ObjectMapper实例

[英]Spring Boot : Different ObjectMapper instances for Request and Response

I have following controller in my spring boot application: 我的Spring Boot应用程序中有以下控制器:

@RequestMapping(method = RequestMethod.POST)
public ResponseEntity<ResponseDto<MyClass> process(@RequestBody RequestDto<MyClass> request){
    return null;
}

MyClass has a field, let's say 'myField' and I want different NamingStrategy configuration for request and response for this field (this is because I don't want to create a new class just for one field). MyClass有一个字段,比方说'myField',我想要该字段的请求和响应使用不同的NamingStrategy配置(这是因为我不想只为一个字段创建一个新类)。 I have configured ObjectMapper instance as below: 我已经配置了ObjectMapper实例,如下所示:

@Bean
public ObjectMapper objectMapper(){
    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.setPropertyNamingStrategy(namingStrategy);
    return objectMapper;
}

This will be used both for Request and Response (ie deserialization and serialization), is there any way in spring boot by which I can instruct the controller to use different ObjectMapper instances? 这将用于请求和响应(即反序列化和序列化),在Spring Boot中有什么方法可以指示控制器使用不同的ObjectMapper实例?

You can solve it with content negotiation . 您可以通过内容协商解决它。 Firstly, define your custom HttpMessageConverter . 首先,定义您的自定义HttpMessageConverter In following example I have defined a custom converter that is applied when the request Content-Type header is set to application/test+json : 在以下示例中,我定义了一个自定义转换器,当请求Content-Type标头设置为application/test+json时将应用该转换器:

@Bean
public HttpMessageConverters customConverters() {
    final AbstractJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(new ObjectMapper());
    converter.setSupportedMediaTypes(Collections.singletonList(MediaType.valueOf("application/test+json")));

    return new HttpMessageConverters(true, Collections.singletonList(converter));
}

For simplicity of this example I've used newly created ObjectMapper - in your case you will have to pass here previously configured object. 为了简化本示例,我使用了新创建的ObjectMapper在您的情况下,您将必须在此处传递先前配置的对象。

Next thing is to tell your action to accept only appliction/test+json requests (keep in mind, that from now on it requires to Content-Type:application/test+json header to present in every request to this endpoint): 接下来的事情是告诉您的操作仅接受appliction/test+json请求(请记住,从现在开始,它要求Content-Type:application/test+json标头出现在对此端点的每个请求中):

@RequestMapping(method = RequestMethod.POST, consumes = "application/test+json")
public ResponseEntity<ResponseDto<MyClass> process(@RequestBody RequestDto<MyClass> request){
    return null;
}

Last thing is to make sure that when you call this endpoint, Content-Type:application/test+json header is set. 最后一件事是确保在调用此终结点时,设置了Content-Type:application/test+json标头。 Of course you can use any other name for desired content type, presented name is just an example. 当然,您可以将其他任何名称用于所需的内容类型,显示名称仅是示例。

You can use a deserialization modifier in your ObjectMapper to override the set of enabled features at object deserialization time via a module. 您可以在ObjectMapper中使用反序列化修饰符,以在对象反序列化时通过模块覆盖已启用的功能集。 This one should do the trick: 这应该可以解决问题:

    public class FeatureModifyingBeanDeserializerModifier extends BeanDeserializerModifier {

        private Collection<Class<?>> modifiedClasses;

        public FeatureModifyingBeanDeserializerModifier(Collection<Class<?>> modifiedClasses) {
            this.modifiedClasses = Collections.unmodifiableSet(new HashSet<Class<?>>(modifiedClasses));
        }

        @Override
        public JsonDeserializer<?> modifyDeserializer(
                DeserializationConfig config, BeanDescription beanDesc, final JsonDeserializer<?> deserializer) {
            JsonDeserializer<?> result = deserializer;
            Class<?> beanClass = beanDesc.getBeanClass();

            if (modifiedClasses.contains(beanClass)) {
                result = new FeatureModifyingStdDeserializer(deserializer, beanClass);
            }
            return result;
        }

        private static class FeatureModifyingStdDeserializer extends StdDeserializer<Object> {

            private JsonDeserializer<?> deserializer;

            private FeatureModifyingStdDeserializer(
JsonDeserializer<?> deserializer, Class<?> beanClass) {
                super(beanClass);
                this.deserializer = deserializer;
            }

            @Override
            public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
                p.enable(JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER);
                return deserializer.deserialize(p, ctxt);
            }
        }      
    }

You have to register it with the ObjectMapper as a module like this: 您必须将其注册到ObjectMapper作为如下模块:

ObjectMapper objectMapper = new ObjectMapper();
SimpleModule module = new SimpleModule();

module.setDeserializerModifier(new FeatureModifyingBeanDeserializerModifer(Arrays.asList(Journey.class)));
objectMapper.registerModule(module);

For serialization, you can add an @JsonSerialize annotation to the Journey class and serialize it in whatever way you want. 对于序列化,您可以向Journey类添加@JsonSerialize批注,并以所需的任何方式对其进行序列化。 If you need to write an unescaped string you can use writeRaw from JsonGenerator. 如果需要编写未转义的字符串,则可以使用JsonGenerator中的writeRaw。

One dirty hack: you may write custom serializer and deserializer for MyClass, there you explicitly use two separate object mapper one for serialization (for response) and second for deserialization (for request). 一个肮脏的技巧:您可以为MyClass编写自定义序列化器和反序列化器,在那里您显式使用两个单独的对象映射器,一个用于序列化(用于响应),第二个用于反序列化(用于请求)。

But it's better to find a way to explicitly customize spring object mapper. 但是最好找到一种显式自定义spring对象映射器的方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM