5 votes

Filtrer les champs et les sous-champs dans Jackson

Pour un peu de 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",
    }
  }
}

Je veux appliquer une liste arborescente de noms de champs. Cela pourrait probablement être exprimé en JSONPath :

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

Ensuite, je devrais obtenir quelque chose comme :

{
  "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",
    }
  }
}

L'idée est que, pour une hiérarchie de champs donnée, je veux limiter les champs qui sont renvoyés.

Je fais cela à travers une bibliothèque qui a annoté les champs de ses objets, mais je ne peux pas modifier la bibliothèque. Cela n'aurait pas vraiment d'importance car la hiérarchie sera sur une base de sérialisation par sérialisation. Actuellement, je passe les objets dans la méthode writeObject de JsonGenerator, mais cela renvoie tout.

Certains sous-objets peuvent partager des noms de champs, donc ce n'est pas aussi simple que de créer un SimpleBeanPropertyFilter pour sérialiser uniquement un ensemble de noms.

Merci d'avance,

John

1voto

Alexey Gavrilov Points 2598

Vous pouvez écrire un filtre de propriété personnalisé qui prendrait en compte la classe déclarante des propriétés sérialisées.

Vous devez étendre le SimpleBeanPropertyFilter et remplacer la méthode include(PropertyWriter writer). Si le paramètre writer donné est une instance de la classe BeanPropertyWriter, vous pouvez extraire les informations sur l'origine de la propriété et appliquer votre logique de filtrage personnalisée.

Voici un exemple de filtre qui stocke les informations d'exclusion dans une carte de classes et de noms de propriétés :

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 list;

        public B(final String field1, final List list) {
            this.field1 = field1;
            this.list = list;
        }
    }
    @JsonFilter("filter")
    public static class Foo {
        public final String field1;
        public final List field2;

        public Foo(final String field1, final List field2) {
            this.field1 = field1;
            this.field2 = field2;
        }
    }

    public static class MyFilter extends SimpleBeanPropertyFilter {
        private final Map, Set> excludePropMap;

        public MyFilter(final Map, Set> 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 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, Set> excludePropMap =
                Collections., Set>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));
    }

}

****Sortie :

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

0voto

ruediste Points 539

J'ai eu besoin d'ajouter une annotation d'ignorance personnalisée à ma sérialisation. Voici ce que j'ai fini par faire

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

et

ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationConfig.Feature.SORT_PROPERTIES_ALPHABETICALLY, true);
mapper.setAnnotationIntrospector(new JacksonAnnotationIntrospector() {
    @Override
    public String[] findPropertiesToIgnore(AnnotatedClass ac) {
        Set 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();

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X