如何动态忽略杰克逊序列化的属性

Posted

技术标签:

【中文标题】如何动态忽略杰克逊序列化的属性【英文标题】:How to dynamically ignore a property on Jackson serialization 【发布时间】:2018-12-12 19:48:57 【问题描述】:

我有一个具有多个 @ManyToOne 关联的实体。我正在使用 spring-boot 来公开一个 REST API。目前,我有多个 REST API,它们返回整个实体的 JSON 响应,包括关联。

但我不想序列化所有 REST API 中的所有关联对象。

例如

API-1 应该返回 parent + associationA 对象 API-2 应该返回父级 + 关联 A + 关联 B 对象 API-3 应该返回父级 + 关联 B + 关联 C + 关联 D

因此,在我的序列化过程中,我想忽略 API-1 的除关联 A 之外的所有关联。

对于 API-2,我想忽略除 A 和 B 之外的其他关联


如何在 Jackson 序列化期间动态忽略这些属性?

注意事项: 我为每个人使用相同的课程;我对为每个 API 创建 DTO 不感兴趣。

任何建议都非常感谢。

【问题讨论】:

我在这里回答***.com/questions/8179986/… 【参考方案1】:

我总结了三种在 Jackson 中执行动态过滤的方法。其中之一必须满足您的需求。

使用@JsonView

你可以使用@JsonView

public class Views          
    interface Simple    
    interface Detailed extends Simple     

public class Foo 

    @JsonView(Views.Simple.class)
    private String name;

    @JsonView(Views.Detailed.class)
    private String details;

    // Getters and setters

@RequestMapping("/foo")
@JsonView(Views.Detailed.class)
public Foo getFoo() 
    Foo foo = new Foo();
    return foo;

您也可以使用MappingJacksonValue 动态设置视图。

@RequestMapping("/foo")
public MappingJacksonValue getFoo() 
    Foo foo = new Foo();
    MappingJacksonValue result = new MappingJacksonValue(foo);
    result.setSerializationView(Views.Detailed.class);
    return result;

使用BeanSerializerModifier

您可以扩展BeanSerializerModifier,然后覆盖changeProperties() 方法。它允许您根据需要添加、删除或替换序列化的任何属性:

public class CustomSerializerModifier extends BeanSerializerModifier 

    @Override
    public List<BeanPropertyWriter> changeProperties(SerializationConfig config,
        BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) 

        // In this method you can add, remove or replace any of passed properties

        return beanProperties;
    

然后将序列化程序注册为您的ObjectMapper 中的模块:

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new SimpleModule() 

    @Override
    public void setupModule(SetupContext context) 
        super.setupModule(context);
        context.addBeanSerializerModifier(new CustomSerializerModifier());
    
);

检查示例here 和here。

使用 @JsonFilterSimpleBeanPropertyFilter

另一种方法涉及@JsonFilter

@JsonFilter("customPropertyFilter")
public class Foo 

    private String name;
    private String details;

    // Getters and setters

根据您的需要扩展SimpleBeanPropertyFilter 并覆盖serializeAsField() 方法:

public class CustomPropertyFilter extends SimpleBeanPropertyFilter 

    @Override
    public void serializeAsField(Object pojo, JsonGenerator jgen,
                                 SerializerProvider provider, 
                                 PropertyWriter writer) throws Exception 

        // Serialize a field
        // writer.serializeAsField(pojo, jgen, provider, writer);

        // Omit a field from serialization
        // writer.serializeAsOmittedField(pojo, jgen, provider);
    

然后在你的ObjectMapper注册过滤器:

FilterProvider filterProvider = new SimpleFilterProvider()
        .addFilter("customPropertyFilter", new CustomPropertyFilter());

ObjectMapper mapper = new ObjectMapper();
mapper.setFilterProvider(filterProvider);

如果你想让你的过滤器“全局”,也就是应用到所有bean,你可以创建一个mix-in类并用@JsonFilter("customPropertyFilter")注解:

@JsonFilter("customPropertyFilter")
public class CustomPropertyFilterMixIn 


然后将mix-in类绑定到Object

mapper.addMixIn(Object.class, CustomPropertyFilterMixIn.class);

【讨论】:

View 也和 dto 类似吧?我必须创建具有关联组合的多个视图类。 @Achaius 您可以定义两个视图:一个视图用于一个 API,另一个视图用于另一个 API。然后在应用程序中返回的所有模型类中重用它们。 不。上面的 API 是我给出的示例。实际上,我有 11 个具有所有关联组合的 API。所以创建 2 个视图还不够。还有其他可能的建议吗? 好的。将检查这一点并让您知道。提前致谢【参考方案2】:

我已经对从 db 获取的数据实现了动态过滤器,并使用 rest api 返回它。我避免使用 MappingJacksonValue。因为它在对象链接时遇到问题

@GetMapping("/courses")
    public ResponseEntity<JpaResponse> allCourse() throws Exception 
        JpaResponse response = null;
         ObjectMapper mapper = new ObjectMapper(); 
         mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        List<Course> course = service.findAllCourse();
        SimpleBeanPropertyFilter filter = SimpleBeanPropertyFilter.filterOutAllExcept("name","reviews");
        FilterProvider filterProvider = new SimpleFilterProvider().addFilter("jpafilter", filter).setFailOnUnknownId(false);
                ObjectWriter writer = mapper.writer(filterProvider);
        String writeValueAsString = writer.writeValueAsString(course);
        List<Course> resultcourse = mapper.readValue(writeValueAsString,List.class);
            response = new JpaResponse(HttpStatus.OK.name(),resultcourse);
            return new ResponseEntity<>(response, HttpStatus.OK);



public class JpaResponse 
        private String status;
        private Object data;
        public JpaResponse() 
            super();
        
        public JpaResponse(String status, Object data) 
            super();
            this.status = status;
            this.data = data;
        

【讨论】:

【参考方案3】:
  public static <T> String getNonNullFieldsSerialized(T object, ObjectMapper objectMapper)throws JsonProcessingException 

    Map<String, Object> objectMap = objectMapper.convertValue(object, new TypeReference<Map<String, Object>>() );

    Map<String, Object> objectMapNonNullValues = objectMap.entrySet().stream()
            .filter(stringObjectEntry -> Objects.nonNull(stringObjectEntry.getValue()))
            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

    return objectMapper.writeValueAsString(objectMapNonNullValues);
  

这基本上会忽略所有非空字段。同样,您可以通过更改地图过滤条件来忽略其他字段。

【讨论】:

您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center。

以上是关于如何动态忽略杰克逊序列化的属性的主要内容,如果未能解决你的问题,请参考以下文章

杰克逊动态属性名称

杰克逊在序列化时忽略@Size

如何在没有jackson注释的情况下忽略反序列化的某些字段?

如何让 Json.Net 在不忽略子属性的情况下从 documentDB 序列化/反序列化动态/通用对象?

杰克逊:我怎么能忽略getter但解析setter? [复制]

杰克逊:忽略getter,但不使用@JsonView