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。
我不喜欢这个解决方案,因为:
- 我必须为每个 DTO 创建一个自定义序列化程序 class 我需要序列化它有 getter 我不想序列化的方法
- 我不喜欢 return 代表 DTO 的 JSON 字符串的解决方案...我更喜欢透明地做到这一点并且 return DTO class 并让 Spring 的自动化生成该转换
提前致谢...任何帮助将不胜感激
编辑(解决方案)
由于@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都可以混进去
- 各种注解(成员方法、静态方法、字段、构造函数注解)都可以混入
- 仅方法(和字段)名称和签名用于匹配注释:访问定义(
private
、protected
、...)和方法实现将被忽略。
有关更多详细信息,请查看 Jackson documentation。
以防万一,如果有人需要单独忽略自定义 getter(没有 setter)
mapper.configure(MapperFeature.REQUIRE_SETTERS_FOR_GETTERS, 真)
在序列化过程中,施展魔法。
我正在尝试连载一个 class,但我不能在上面触摸任何东西。问题是我想忽略一些 getWhatever() getter 方法,但我不能用 @JsonIgnore[ 标记 getter 方法,因为这意味着触及 DTO class。
该序列化是通过 Spring REST Web 服务中的 @RestController 方法进行的,所以如果它能成为考虑到这一点的解决方案,那就太好了.
我想到了一个我不喜欢的解决方案...它会为该 DTO 创建一个自定义序列化程序 class我想序列化,这样我就可以控制序列化的内容和不序列化的内容,然后,从 @RestController,而不是 returning DTO class(我认为这更优雅),return在使用 ObjectMapper 获取 JSON 字符串并强制自定义序列化程序后,输入 String。
我不喜欢这个解决方案,因为:
- 我必须为每个 DTO 创建一个自定义序列化程序 class 我需要序列化它有 getter 我不想序列化的方法
- 我不喜欢 return 代表 DTO 的 JSON 字符串的解决方案...我更喜欢透明地做到这一点并且 return DTO class 并让 Spring 的自动化生成该转换
提前致谢...任何帮助将不胜感激
编辑(解决方案) 由于@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都可以混进去
- 各种注解(成员方法、静态方法、字段、构造函数注解)都可以混入
- 仅方法(和字段)名称和签名用于匹配注释:访问定义(
private
、protected
、...)和方法实现将被忽略。
有关更多详细信息,请查看 Jackson documentation。
以防万一,如果有人需要单独忽略自定义 getter(没有 setter) mapper.configure(MapperFeature.REQUIRE_SETTERS_FOR_GETTERS, 真)
在序列化过程中,施展魔法。