Jackson 从 class 序列化中忽略 getter

Jackson ignore getter from the class serializing

我正在尝试连载一个 class,但我不能在上面触摸任何东西。问题是我想忽略一些 getWhatever() getter 方法,但我不能用 @JsonIgnore[ 标记 getter 方法,因为这意味着触及 DTO class。

该序列化是通过 Spring REST Web 服务中的 @RestController 方法进行的,所以如果它能成为考虑到这一点的解决方案,那就太好了.

我想到了一个我不喜欢的解决方案...它会为该 DTO 创建一个自定义序列化程序 class我想序列化,这样我就可以控制序列化的内容和不序列化的内容,然后,从 @RestController,而不是 returning DTO class(我认为这更优雅),return在使用 ObjectMapper 获取 JSON 字符串并强制自定义序列化程序后,输入 String

我不喜欢这个解决方案,因为:

提前致谢...任何帮助将不胜感激

编辑(解决方案) 由于@Cassio Mazzochi Molin 的想法,我终于采用了这个解决方案:

interface FooMixIn {
    @JsonIgnore
    Object getFoo();
    @JsonIgnore
    Object getBar();
}

@Service
public class ServiceFooSerializer extends SimpleModule{
    public ServiceFooSerializer(){
        this.setMixInAnnotation(Foo.class, FooMixIn.class);
    }
}

this answer and based on the Spring Boot Example

的帮助下

这是 DTO1 和 DTO2 class,它们看起来一样(getter 和 setter 未显示):

public class DTO1 {

    private String property1;
    private String property2;
    private String property3;

这是一个用于测试的控制器:

@RestController
public class HelloController {

    @RequestMapping("/dto1")
    public ResponseEntity<DTO1> dto1() {
        return new ResponseEntity<DTO1>(new DTO1("prop1", "prop2", "prop3"), HttpStatus.OK);
    }

    @RequestMapping("/dto2")
    public ResponseEntity<DTO2> dto2() {
        return new ResponseEntity<DTO2>(new DTO2("prop1", "prop2", "prop3"), HttpStatus.OK);
    }

}

这是我的 WebConfig

@Configuration
public class WebConfig extends WebMvcConfigurationSupport {

    @Bean
    public MappingJackson2HttpMessageConverter customJackson2HttpMessageConverter() {
        MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
        ObjectMapper objectMapper = new ObjectMapper();
        MyBeanSerializerFactory customSerializationFactory = new MyBeanSerializerFactory(new SerializerFactoryConfig());
        customSerializationFactory.getClasses().add(DTO1.class);
        customSerializationFactory.getFieldsToIgnore().add("property2");
        objectMapper.setSerializerFactory(customSerializationFactory);
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        jsonConverter.setObjectMapper(objectMapper);
        return jsonConverter;
    }

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

这是自定义的 BeanserializerFactory:

public class MyBeanSerializerFactory extends BeanSerializerFactory {

    private Set<Class> classes = new HashSet<>();
    private Set<String> fieldsToIgnore = new HashSet<>();

    protected MyBeanSerializerFactory(SerializerFactoryConfig config) {
        super(config);
        // TODO Auto-generated constructor stub
    }

    public Set<Class> getClasses() {
        return classes;
    }

    public Set<String> getFieldsToIgnore() {
        return fieldsToIgnore;
    }

    @Override
    protected void processViews(SerializationConfig config, BeanSerializerBuilder builder) {
        super.processViews(config, builder);

        // ignore fields only for concrete class
        // note, that you can avoid or change this check
        if (classes.contains(builder.getBeanDescription().getBeanClass())) {
            // get original writer
            List<BeanPropertyWriter> originalWriters = builder.getProperties();

            // create actual writers
            List<BeanPropertyWriter> writers = new ArrayList<BeanPropertyWriter>();

            for (BeanPropertyWriter writer : originalWriters) {
                String propName = writer.getName();
                // if it isn't ignored field, add to actual writers list
                if (!fieldsToIgnore.contains(propName)) {
                    writers.add(writer);
                }
            }

            builder.setProperties(writers);
        }

    }
}

这是一个测试,它显示从 DTO1 中删除了 property2,但没有从 DTO2 中删除:

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class HelloControllerTest {

    @Autowired
    private MockMvc mvc;

    @Test
    public void test1() throws Exception {
        mvc.perform(MockMvcRequestBuilders.get("/dto1").accept(MediaType.APPLICATION_JSON)).andExpect(status().isOk())
                .andExpect(content().string(equalTo("{\"property1\":\"prop1\",\"property3\":\"prop3\"}")));

        mvc.perform(MockMvcRequestBuilders.get("/dto2").accept(MediaType.APPLICATION_JSON)).andExpect(status().isOk())
                .andExpect(content()
                        .string(equalTo("{\"property1\":\"prop1\",\"property2\":\"prop2\",\"property3\":\"prop3\"}")));
    }
}

请参阅 https://github.com/gregclinker/json-mapper-example 了解上面的代码。

当修改 classes 不是一个选项时,您可以使用 mix-in 注释

您可以将其视为一种 aspect-oriented 在运行时添加更多注释的方式,以扩充静态定义的注释。

首先定义一个mix-in注解接口(class也可以):

public interface FooMixIn {

    @JsonIgnore
    Object getWhatever();
}

然后配置 ObjectMapper 以将定义的接口用作您的 POJO 的 mix-in:

ObjectMapper mapper = new ObjectMapper().addMixIn(Foo.class, FooMixIn.class); 

一些使用注意事项:

  • Jackson认识的annotation sets都可以混进去
  • 各种注解(成员方法、静态方法、字段、构造函数注解)都可以混入
  • 仅方法(和字段)名称和签名用于匹配注释:访问定义(privateprotected、...)和方法实现将被忽略。

有关更多详细信息,请查看 Jackson documentation

以防万一,如果有人需要单独忽略自定义 getter(没有 setter) mapper.configure(MapperFeature.REQUIRE_SETTERS_FOR_GETTERS, 真)

在序列化过程中,施展魔法。