Java – the custom Jackson httpmessageconverter is no longer applicable to spring 4.2

I'm starting from spring platform version 1.1 3. Release update the application to 2.0 1. Release, which changes the Spring Framework version from 4.1 7 is changed to 4.2 4, and Jackson from 2.4 6 to 2.6 4. There seems to be no significant change in spring or Jackson's handling of the implementation of custom httpmessageconverter, but my custom JSON serialization failed to occur. I'm not sure why In previous versions of the spring platform, the following worked correctly:

Model

@JsonFilter("fieldFilter")
public class MyModel { 
    /*model fields and methods*/ 
}

Model packaging

public class ResponseEnvelope {

    private Set<String> fieldSet;
    private Set<String> exclude;
    private Object entity;

    public ResponseEnvelope(Object entity) {
        this.entity = entity;
    }

    public ResponseEnvelope(Object entity,Set<String> fieldSet,Set<String> exclude) {
        this.fieldSet = fieldSet;
        this.exclude = exclude;
        this.entity = entity;
    }

    public Object getEntity() {
        return entity;
    }

    @JsonIgnore
    public Set<String> getFieldSet() {
        return fieldSet;
    }

    @JsonIgnore
    public Set<String> getExclude() {
        return exclude;
    }

    public void setExclude(Set<String> exclude) {
        this.exclude = exclude;
    }

    public void setFieldSet(Set<String> fieldSet) {
        this.fieldSet = fieldSet;
    }

    public void setFields(String fields) {
        Set<String> fieldSet = new HashSet<String>();
        if (fields != null) {
            for (String field : fields.split(",")) {
                fieldSet.add(field);
            }
        }
        this.fieldSet = fieldSet;
    }
}

Regulator

@Controller
public class MyModelController {

    @Autowired MyModelRepository myModelRepository;

    @RequestMapping(value = "/model",method = RequestMethod.GET,produces = { MediaType.APPLICATION_JSON_VALUE })
    public httpentity find(@RequestParam(required=false) Set<String> fields,@RequestParam(required=false) Set<String> exclude){
        List<MyModel> objects = myModelRepository.findAll();
        ResponseEnvelope envelope = new ResponseEnvelope(objects,fields,exclude);
        return new ResponseEntity<>(envelope,HttpStatus.OK);
    }
}

Custom httpmessageconverter

public class FilteringJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {

    private boolean prefixJson = false;

    @Override
    public void setPrefixJson(boolean prefixJson) {
        this.prefixJson = prefixJson;
        super.setPrefixJson(prefixJson);
    }

    @Override
    protected void writeInternal(Object object,HttpOutputMessage outputMessage)
            throws IOException,HttpMessageNotWritableException {

        ObjectMapper objectMapper = getObjectMapper();
        JsonGenerator jsonGenerator = objectMapper.getFactory().createGenerator(outputMessage.getBody());

        try {

            if (this.prefixJson) {
                jsonGenerator.writeRaw(")]}',");
            }

            if (object instanceof ResponseEnvelope) {

                ResponseEnvelope envelope = (ResponseEnvelope) object;
                Object entity = envelope.getEntity();
                Set<String> fieldSet = envelope.getFieldSet();
                Set<String> exclude = envelope.getExclude();
                FilterProvider filters = null;

                if (fieldSet != null && !fieldSet.isEmpty()) {
                    filters = new SimpleFilterProvider()
                            .addFilter("fieldFilter",SimpleBeanPropertyFilter.filterOutAllExcept(fieldSet))
                                .setFailOnUnkNownId(false);
                } else if (exclude != null && !exclude.isEmpty()) {
                    filters = new SimpleFilterProvider()
                            .addFilter("fieldFilter",SimpleBeanPropertyFilter.serializeAllExcept(exclude))
                                .setFailOnUnkNownId(false);
                } else {
                    filters = new SimpleFilterProvider()
                            .addFilter("fieldFilter",SimpleBeanPropertyFilter.serializeAllExcept())
                                .setFailOnUnkNownId(false);
                }

                objectMapper.setFilterProvider(filters);
                objectMapper.writeValue(jsonGenerator,entity);

            } else if (object == null){
                jsonGenerator.writeNull();
            } else {
                FilterProvider filters = new SimpleFilterProvider().setFailOnUnkNownId(false);
                objectMapper.setFilterProvider(filters);
                objectMapper.writeValue(jsonGenerator,object);
            }

        } catch (JsonProcessingException e){
            e.printStackTrace();
            throw new HttpMessageNotWritableException("Could not write JSON: " + e.getMessage());
        }

    }
}

configuration

@Configuration
@EnableWebMvc
public class WebServicesConfig extends WebMvcConfigurerAdapter {

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        FilteringJackson2HttpMessageConverter jsonConverter = new FilteringJackson2HttpMessageConverter();
        jsonConverter.setSupportedMediaTypes(MediaTypes.APPLICATION_JSON);
        converters.add(jsonConverter);
    }

    // Other configurations
}

Now I get this exception (recorded by spring), and there is a 500 error when making any request:

[main] WARN  o.s.w.s.m.s.DefaultHandlerExceptionResolver - Failed to write HTTP message: 
  org.springframework.http.converter.HttpMessageNotWritableException: Could not write content: 
  Can not resolve PropertyFilter with id 'fieldFilter'; 
  no FilterProvider configured (through reference chain:
  org.oncoblocks.centromere.web.controller.ResponseEnvelope["entity"]->java.util.ArrayList[0]); 
  nested exception is com.fasterxml.jackson.databind.JsonMappingException: 
  Can not resolve PropertyFilter with id 'fieldFilter'; 
  no FilterProvider configured (through reference chain: 
  org.oncoblocks.centromere.web.controller.ResponseEnvelope["entity"]->java.util.ArrayList[0])

The configuremessageconverters method executes, but it does not look like a custom converter is used during the request Another message converter may prevent this message from forwarding to my response? My understanding is that overriding configuremessageconverters will block converters other than manually registered converters

Except for updating dependencies through the spring platform, there is no change between the working and non - working versions of this code What's the change in JSON serialization? Did I just lose it in the document?

edit

Further tests produced strange results I want to test and check the following:

Is my custom httpmessageconverter registering? > Does another converter overwrite / replace it? > Is this my test setup problem?

So I added an additional test and looked at the output:

@Autowired WebApplicationContext webApplicationContext;

@Before
public void setup(){
    mockmvc = mockmvcBuilders.webAppContextSetup(webApplicationContext).build();
}

@Test
public void test() throws Exception {
    RequestMappingHandlerAdapter adapter = (RequestMappingHandlerAdapter) webApplicationContext.getBean("requestMappingHandlerAdapter");
    List<EntrezGene> genes = EntrezGene.createDummyData();
    Set<String> exclude = new HashSet<>();
    exclude.add("entrezGeneId");
    ResponseEnvelope envelope = new ResponseEnvelope(genes,new HashSet<String>(),exclude);
    for (HttpMessageConverter converter: adapter.getMessageConverters()){
        System.out.println(converter.getClass().getName());
        if (converter.canWrite(ResponseEnvelope.class,MediaType.APPLICATION_JSON)){
            MockHttpOutputMessage message =  new MockHttpOutputMessage();
            converter.write((Object) envelope,MediaType.APPLICATION_JSON,message);    
            System.out.println(message.getBodyAsString());
        }
    }
}

... it works properly My envelope object and its contents are serialized and properly filtered Therefore, before reaching the message converter, there is a problem with the request processing, or mockmvc how to test the request has changed

Solution

Your configuration is OK. The reason why writeinternal() is not called from your custom converter is because you have overwritten the wrong method

Look at 4.2 4. Release source code

AbstractMessageConverterMethodProcessor#writeWithMessageConverters

protected <T> void writeWithMessageConverters(T returnValue,MethodParameter returnType,ServletServerHttpRequest inputMessage,ServletServerHttpResponse outputMessage)
            throws IOException,HttpMediaTypeNotAcceptableException,HttpMessageNotWritableException {
    ...
    ((GenericHttpMessageConverter<T>) messageConverter).write(returnValue,returnValueType,selectedMediaType,outputMessage);
    ...
}

Abstractgenerichttpmessageconverter # write

public final void write(final T t,final Type type,MediaType contentType,HttpMessageNotWritableException {
    ...
    writeInternal(t,type,outputMessage);
    ...
}

The writeInternal (...) method invoked from AbstractGenericHttpMessageConverter#write (...) has three parameters - (T T, Type type, HttpOutputMessage outputMessage). You will overwrite the overloaded version (T, httpoutputmessage, outputmessage) of writeinternal (...) with only 2 parameters

However, in version 4.1 7. In release, this is not the case, so it is the root cause of your problem Writeinternal (...) used in this version is another overloaded method (method with 2 parameters). You have been overwritten This explains why it is in 4.1 7. Normal operation in release

@Override
public final void write(final T t,outputMessage);
    ...
}

Therefore, in order to solve your problem, instead of overwriting writeinternal (object object, httpoutputmessage, outputmessage), rewrite writeinternal (object object, type, httpoutputmessage, outputmessage)

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>