【问题标题】:Filtering fields and sub-fields in Jackson过滤 Jackson 中的字段和子字段
【发布时间】:2012-10-03 15:40:10
【问题描述】:

对于一些 JSON:

{
  "id":123,
  "name":"Test",
  "sub_object":{
    "sub_field_1":1,
    "sub_field_2":2,
    "sub_field_array":[
      {
        "array_field_1":true,
        "array_field_2":false
      },
      {
        "array_field_1":false,
        "array_field_2":true
      }
    ],
    "sub_sub_object":{
      "field_1":"me",
      "field_2":"myself",
      "field_3":"i",
    }
  }
}

我想应用一个树状的字段名称列表。这可能用 JSONPath 表示:

root
  |-id
  |-sub_object
    |-sub_field_2
    |-sub_field_array
      |-array_field_1
    |-sub_sub_object

那么我应该得到类似的东西:

{
  "id":123,
  "sub_object":{
    "sub_field_2":2,
    "sub_field_array":[
      {
        "array_field_1":true
      },
      {
        "array_field_1":false
      }
    ],
    "sub_sub_object":{
      "field_1":"me",
      "field_2":"myself",
      "field_3":"i",
    }
  }
}

这个想法是,对于某些字段层次结构,我想限制输出的字段。

我通过一个已注释其对象字段的库来执行此操作,但我无法修改该库。我是否可以这样做并不重要,因为层次结构将基于每个序列化。目前,我将对象传递给 JsonGenerator 的 writeObject 方法,但这会返回所有内容。

有些子对象可能会共享字段名,所以并不像创建一个 SimpleBeanPropertyFilter 来只序列化一组名称那么简单。

提前谢谢你,

约翰

【问题讨论】:

    标签: json serialization filter field jackson


    【解决方案1】:

    您可以编写一个自定义属性过滤器,该过滤器将考虑到序列化属性的声明类。

    您应该扩展SimpleBeanPropertyFilter 并覆盖include(PropertyWriter writer) 方法。如果给定的writer 参数是BeanPropertyWriter 类的实例,您可以提取有关属性来源的信息并应用您的自定义过滤逻辑。

    这是一个过滤器示例,它将排除信息存储在类及其属性名称的映射中:

    public class JacksonHierarchyFilter {
        @JsonFilter("filter")
        public static class A {
            public final String field1;
    
            public A(final String field1) {this.field1 = field1;}
        }
    
        @JsonFilter("filter")
        public static class B {
            public final String field1;
            public final List<A> list;
    
            public B(final String field1, final List<A> list) {
                this.field1 = field1;
                this.list = list;
            }
        }
        @JsonFilter("filter")
        public static class Foo {
            public final String field1;
            public final List<B> field2;
    
            public Foo(final String field1, final List<B> field2) {
                this.field1 = field1;
                this.field2 = field2;
            }
        }
    
        public static class MyFilter extends SimpleBeanPropertyFilter {
            private final Map<Class<?>, Set<String>> excludePropMap;
    
            public MyFilter(final Map<Class<?>, Set<String>> excludePropMap) {
                this.excludePropMap = excludePropMap;
            }
    
            @Override
            protected boolean include(final BeanPropertyWriter writer) {
                return false;
            }
    
            @Override
            protected boolean include(final PropertyWriter writer) {
                if (writer instanceof BeanPropertyWriter) {
                    final Class<?> cls = ((BeanPropertyWriter) writer).getMember().getDeclaringClass();
                    final Set<String> excludePropSet = excludePropMap.get(cls);
                    return excludePropSet == null || !excludePropSet.contains(writer.getName());
                }
                return true;
            }
        }
    
        public static void main(String[] args) throws JsonProcessingException {
            final B b = new B("B1", Arrays.asList(new A("A1"), new A("A2")));
            final Foo foo = new Foo("foo", Arrays.asList(b));
            final ObjectMapper mapper = new ObjectMapper();
            final SimpleFilterProvider filters = new SimpleFilterProvider();
            final Map<Class<?>, Set<String>> excludePropMap =
                    Collections.<Class<?>, Set<String>>singletonMap(
                            B.class,
                            Collections.singleton("field1"));
            filters.addFilter("filter", new MyFilter(excludePropMap));
            mapper.setFilters(filters);
            final ObjectWriter objectWriter = mapper.writerWithDefaultPrettyPrinter();
            System.out.println(objectWriter.writeValueAsString(foo));
        }
    
    }
    

    输出:

    {
      "field1" : "foo",
      "field2" : [ {
        "list" : [ {
          "field1" : "A1"
        }, {
          "field1" : "A2"
        } ]
      } ]
    }
    

    【讨论】:

      【解决方案2】:

      我需要在我的序列化中添加一个自定义忽略注释。我的结局是

      @Retention(RetentionPolicy.RUNTIME)
      @Target(ElementType.FIELD)
      @Documented
      public @interface Unsigned {}
      

      ObjectMapper mapper = new ObjectMapper();
      mapper.configure(SerializationConfig.Feature.SORT_PROPERTIES_ALPHABETICALLY, true);
      mapper.setAnnotationIntrospector(new JacksonAnnotationIntrospector() {
          @Override
          public String[] findPropertiesToIgnore(AnnotatedClass ac) {
              Set<String> result = new HashSet<>();
              for (AnnotatedField field : ac.fields()) {
                  if (field.getAnnotated().isAnnotationPresent(Unsigned.class)) {
                      result.add(field.getName());
                  }
              }
              String[] tmp = super.findPropertiesToIgnore(ac);
              if (tmp != null) {
                  result.addAll(Arrays.asList(tmp));
              }
              return result.toArray(new String[] {});
          }
      });
      ObjectWriter writer = mapper.writer();
      

      【讨论】:

        猜你喜欢
        • 2018-09-28
        • 1970-01-01
        • 1970-01-01
        • 2021-01-02
        • 1970-01-01
        • 2013-12-20
        • 2017-05-10
        • 2017-03-08
        • 1970-01-01
        相关资源
        最近更新 更多