2012-10-12 3 views
4

Pour certains JSON:champs de filtrage et des sous-champs Jackson

{ 
    "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 des noms de champs. Cela pourrait probablement être exprimée en JSONPath:

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

alors je devrais retourner 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 sur le terrain, je veux limiter les champs qui sont émis. Je le fais à travers une bibliothèque qui a annoté les champs de ses objets, mais je ne peux pas modifier la bibliothèque. Ce ne serait pas vraiment important si je pouvais parce que la hiérarchie sera sur une base par sérialisation. Actuellement, je passe les objets dans la méthode writeObject de JsonGenerator, mais cela retourne tout.

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

Nous vous remercions à l'avance,

John

Répondre

1

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

Vous devez étendre le SimpleBeanPropertyFilter et remplacer la méthode include(PropertyWriter writer). Si le paramètre writer donné est une instance de 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 d'un filtre qui stocke les informations exclure sur une carte des classes et leurs noms de propriété:

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)); 
    } 

} 

Sortie:

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

Eh oui! Cela l'a fait pour moi. Merci –

0

je devais ajouter une coutume ignorer l'annotation à ma sérialisation. Ce que je fini était

@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<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(); 
Questions connexes