2012-09-06 2 views
5

JSON文字列を入力として受け取るBeanオブジェクトを更新するREST WSがあります。ジャーナル非直列化中のプロパティの動的フィルタリング

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(...)は豆と埋め込みオブジェクトを保存します。

セキュリティ上の理由から、JSON文字列で更新できるプロパティをいくつか除外したいが、これを動的に実行して、すべてのWS(またはユーザーロール)に対して防止するプロパティを決定できるようにしたい(私は単純にジャクソンビューを使用することはできません)

要約すると、JSON逆シリアル化中にプロパティを動的にフィルタリングする方法はありますか?

答えて

5

もう一つの方法は、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にmodfierを登録することができます。そして、定義されたプロパティは無視され

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メソッドは無視できます。

挨拶、 マーティン

+0

ありがとう!私は元のポスターと同じ問題に遭遇し、クラスタイプに基づいてプロパティを選択的に削除することをサポートしているので、これが受け入れられる回答であるべきだと考えています。 – mseddon

1

私は、特定のクラスごとにフィールドがシリアル化されないように、単に@JsonIgnore注釈を使用することはできません。

Jakson mix-insを参照してください。mix-insを使用すると、データバインディングにクラス定義を必要な注釈で置き換えることができます。シリアライズ中に、にある特定のミックスインクラス定義を選択して、実際のクラスがシリアライズされるようににすることができます。すべてのケースを処理するための一連のミックスインを定義し、特定のBeanを直列化するときに適切なものを選択します。

+0

は、私は、これは私がビューを使用して取得することができ、同じ力を持っていると思います。権限と一緒にデータベースから抽出した動的ロールのセットを持っていて、それらのルールに基づいてフィルタリングしたいのですが? – Mario

1

@JsonIgnoreTypeを使用すると、Javaクラス/ interface/enumをシリアライズするのを無視できます。さらに、@ JsonIgnorePropertiesを使用してプロパティのリストを無視し、特定のプロパティの@JsonIgnoreを使用することができます。

+0

これは静的(コンパイル時)フィルタリングの場合にのみ有効です – Mario

0

私が思いついた最も強力で簡単かつ簡単なソリューションは、逆シリアル化から得られた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); 
     } 
    } 
} 
関連する問題