【问题标题】:Jackson Dynamic filtering of properties during deserializationJackson 反序列化期间属性的动态过滤
【发布时间】:2012-08-31 14:09:02
【问题描述】:

我有一个 REST WS 来更新一个接收 JSON 字符串作为输入的 bean 对象。

ABean entity = svc.findEntity(...);
objectMapper.readerForUpdating(entity).readValue(json);
[...]
svc.save(entity);

ABean 是一种复杂类型,还包含其他对象 例如:

class ABean {
    public BBean b;
    public CBean c;

    public String d;
}

svc.save(...) 将保存 bean 和嵌入的对象

出于安全原因,我想过滤掉一些可以由 JSON 字符串更新的属性,但我想动态地执行此操作,以便对于每个 WS(或用户角色)我可以决定阻止哪些属性更新(所以我不能简单地使用杰克逊观点)

总而言之,有什么方法可以在 JSON 反序列化期间动态过滤掉属性?

【问题讨论】:

    标签: java json filtering jackson deserialization


    【解决方案1】:

    另一种方法是使用 BeanDeserializerModifier:

    private static class BeanDeserializerModifierForIgnorables extends BeanDeserializerModifier {
    
            private java.lang.Class<?> type;
            private List<String> ignorables;
    
            public BeanDeserializerModifierForIgnorables(java.lang.Class clazz, String... properties) {
                ignorables = new ArrayList<>();
                for(String property : properties) {
                    ignorables.add(property);
                }
                this.type = clazz;
            }
    
            @Override
            public BeanDeserializerBuilder updateBuilder(
                    DeserializationConfig config, BeanDescription beanDesc,
                    BeanDeserializerBuilder builder) {
                if(!type.equals(beanDesc.getBeanClass())) {
                    return builder;
                }
    
                for(String ignorable : ignorables) {
                    builder.addIgnorable(ignorable);                
                }
    
                return builder;
            }
    
            @Override
            public List<BeanPropertyDefinition> updateProperties(
                    DeserializationConfig config, BeanDescription beanDesc,
                    List<BeanPropertyDefinition> propDefs) {
                if(!type.equals(beanDesc.getBeanClass())) {
                    return propDefs;
                }
    
                List<BeanPropertyDefinition> newPropDefs = new ArrayList<>();
                for(BeanPropertyDefinition propDef : propDefs) {
                    if(!ignorables.contains(propDef.getName())) {
                        newPropDefs.add(propDef);
                    }
                }
                return newPropDefs;
            }
        }
    

    您可以使用以下方法将修改器注册到 ObjectMapper:

    BeanDeserializerModifier modifier = new BeanDeserializerModifierForIgnorables(YourType.class, "name");
    DeserializerFactory dFactory = BeanDeserializerFactory.instance.withDeserializerModifier(modifier);
    ObjectMapper mapper = new ObjectMapper(null, null, new DefaultDeserializationContext.Impl(dFactory));
    

    然后忽略定义的属性。如果使用 @JsonAnySetter 注解,可以忽略 updateBuilder 方法。

    您好, 马丁

    【讨论】:

    • 谢谢!我遇到了与原始海报相同的问题,并认为这应该是公认的答案,因为它支持根据类类型选择性地删除属性。
    • 如何在 Jersey 注册修饰符?
    【解决方案2】:

    根据您的描述,我假设您不能简单地使用 @JsonIgnore 注释来防止为每个特定类序列化字段。

    看看使用Jakson mix-ins:mix-ins 允许你用必要的注解替换一个类定义来进行数据绑定。在序列化过程中,您可以选择一个特定的混合类定义来代替实际被序列化的类。定义一组 mix-ins 来处理每种情况,然后在序列化特定 bean 时选择合适的。

    【讨论】:

    • 我认为这具有与使用视图相同的功能。如果我有一组从数据库中提取的动态角色及其权限,并且我想根据这些规则进行过滤,该怎么办?
    【解决方案3】:

    您可以使用@JsonIgnoreType 从序列化中忽略 java 类/接口/枚举。此外,您可以使用 @JsonIgnoreProperties 来忽略属性列表,使用 @JsonIgnore 来忽略特定属性

    【讨论】:

    • 这仅对静态(编译时)过滤有效
    【解决方案4】:

    我想出的最强大、最快捷、最简单的解决方案就是对反序列化得到的JsonNode树进行过滤,然后将过滤后的结果传递给readerForUpdating。类似的东西:

    public class JacksonHelper {
        public JsonNode filterBeanTree(JsonNode o, List<String> includedProperties,
                List<String> excludedProperties, int maxDepth) {
            JsonNode tree = o.deepCopy();
            this.filterBeanTreeRecursive(tree, includedProperties, excludedProperties, maxDepth, null);
            return tree;
        }
    
        private void filterBeanTreeRecursive(JsonNode tree, List<String> includedProperties,
                List<String> excludedProperties, int maxDepth, String key) {
            Iterator<Entry<String, JsonNode>> fieldsIter = tree.fields();
            while (fieldsIter.hasNext()) {
                Entry<String, JsonNode> field = fieldsIter.next();
                String fullName = key == null ? field.getKey() : key + "." + field.getKey();
    
                boolean depthOk = field.getValue().isContainerNode() && maxDepth >= 0;
                boolean isIncluded = includedProperties != null
                        && !includedProperties.contains(fullName);
                boolean isExcluded = excludedProperties != null
                        && excludedProperties.contains(fullName);
                if ((!depthOk && !isIncluded) || isExcluded) {
                    fieldsIter.remove();
                    continue;
                }
    
                this.filterBeanTreeRecursive(field.getValue(), includedProperties, excludedProperties,
                        maxDepth - 1, fullName);
            }
        }
    } 
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-13
      • 1970-01-01
      • 1970-01-01
      • 2018-10-31
      • 1970-01-01
      相关资源
      最近更新 更多