2016-09-29 62 views
6

我正在使用Spring版本4.3.3和Jackson版本2.8.3。我想基於一些在運行時確定的自定義邏輯從實體bean中篩選出特定的字段。 @JsonFilter似乎是這種類型的功能的理想選擇。問題是,當我把它放在字段或方法級別時,我的自定義過濾器從不被調用。如果我把它放在課堂上,它會被調用得很好。我不想在課堂上使用它,但從那時起,我需要單獨維護我想要應用邏輯的硬編碼字段名稱列表。從傑克遜2.3開始,將這個註釋放在現場級別的能力應該存在。Jackson @JsonFilter在字段或方法級別使用時未得到應用

這裏是沒有任何自定義邏輯最基本的自定義過濾器尚未:

public class MyFilter extends SimpleBeanPropertyFilter { 

@Override 
protected boolean include(BeanPropertyWriter beanPropertyWriter) { 
    return true; 
} 

@Override 
protected boolean include(PropertyWriter propertyWriter) { 
    return true; 
} 

}

然後,我有傑克遜ObjectMapper配置:

public class MyObjectMapper extends ObjectMapper { 

    public MyObjectMapper() { 
     SimpleFilterProvider filterProvider = new SimpleFilterProvider(); 
     filterProvider.addFilter("myFilter", new MyFilter()); 
     setFilterProvider(filterProvider); 
    } 
} 

於是最後我有我的實體豆:

@Entity 
public class Project implements Serializable { 

    private Long id; 
    private Long version; 
    @JsonFilter("myFilter") private String name; 
    @JsonFilter("myFilter") private String description; 

    // getters and setters 

} 

如果我將@JsonFilter註釋移動到@Entity所在的類級別,則至少會調用該過濾器,但當它處於字段級別時(如本例中的示例級別),它永遠不會被調用。

回答

4

我有同樣的需要,但在檢查單元測試後,我發現這不是註釋字段所涉及的用例。

註釋字段將調用對字段值的過濾器,而不是包含該字段的實例。例如,假設你有類,A和B,其中A包含B型的領域

class A { 
    @JsonFilter("myFilter") B foo; 
} 

傑克遜適用「myfilter」的在B中的字段不是A.由於您的示例包含類型的字段字符串,沒有字段,傑克遜永遠不會調用你的過濾器。

+1

我正在研究使用自定義AnnotationIntrospector來處理此用例的可能性。如果我可以得到它的工作,我會添加另一個答案。 – Faron

+0

你得到它的工作? –

+0

@Vinit Gaikwad他不能這樣做,因爲每個班級都會發生一次內省。您將無法通過AnnotationIntrospector動態確定邏輯 –

0

你可以試試這個方法爲同樣的目的:

@Entity 
@Inheritance(
    strategy = InheritanceType.SINGLE_TABLE 
) 
@DiscriminatorColumn(
    discriminatorType = DiscriminatorType.STRING, 
    length = 2 
) 
@Table(
    name = "project" 
) 
@JsonTypeInfo(
    use = Id.CLASS, 
    include = As.PROPERTY, 
    property = "@class" 
) 
@JsonSubTypes({ 
    @Type(
     value = BasicProject.class, 
     name = "basicProject" 
    ), 
    @Type(
     value = AdvanceProject.class, 
     name = "advanceProject" 
    )}) 
public abstract class Project { 
    private Long id; 
    private Long version; 

} 

@Entity 
@DiscriminatorValue("AD") 
public class AdvanceProject extends Project { 
    private String name; 
    private String description; 
} 

@Entity 
@DiscriminatorValue("BS") 
public class BasicProject extends Project { 
    private String name; 
} 
0

我不認爲你會使其工作。我正在嘗試,這些都是我調查的結果,也許會有幫助。

首先,@Faron注意到,@JsonFilter註釋適用於被註釋的類而不是字段。

其次,我看到這樣的事情。讓我們想象一下,在傑克遜內部的某個地方,你可以獲得實際的領域。您可以使用Java Reflection API確定是否存在註釋。你甚至可以獲取過濾器名稱。然後你到達過濾器並在那裏傳遞字段值。但是它發生在運行時,如果您決定序列化字段,您將如何獲得字段類型的相應JsonSerializer?由於類型擦除是不可能的。

我看到的唯一選擇是忘記動態邏輯。然後你可以做以下的事情:

1)擴展JacksonAnnotationIntrospector(幾乎相同的實現AnnotationIntrospector但沒有無用的默認代碼)覆蓋hasIgnoreMarker方法。看看this answer

2)罪犯從這裏開始。有點奇怪的方式考慮到你的初始目標,但仍然:擴展​​並過濾掉那裏的領域。一個例子可以發現here。這類似於上面的方法方式,您可以定義序列化,實際上不序列東西(再次,我明白這是多麼奇怪,但也許人們會發現它有用)

3):定義基於BeanDescription實施ContextualSerializer沒用串行的createContextual方法。這個神奇的例子是here

0

我有需要根據調用者的權限排除某些字段。例如,員工的個人資料可能包含他的納稅人ID,這被認爲是敏感信息,並且只有在主叫方是Payrole部門的成員時才應該序列化。由於我使用的是Spring Security,因此我希望將Jackson與當前的安全上下文整合。

public class EmployeeProfile { 
    private String givenName; 
    private String surname; 
    private String emailAddress; 

    @VisibleWhen("hasRole('PayroleSpecialist')") 
    private String taxpayerId; 
} 

最明顯的方法做,這是傑克遜的過濾機制,但它有一些限制:

  1. 傑克遜不支持嵌套的過濾器,以便將訪問過濾器使用過濾器用於其他任何用途禁止。
  2. 不能將Jackson註釋添加到現有的第三方類。
  3. 傑克遜過濾器的設計不是通用的。目的是爲每個想要應用過濾的類編寫一個自定義過濾器。例如,我需要篩選類A和B,然後您必須編寫一個AFilter和一個BFilter。

對於我的用例,解決方案是將自定義註釋introspector與鏈接過濾器結合使用。

public class VisibilityAnnotationIntrospector extends JacksonAnnotationIntrospector { 
    private static final long serialVersionUID = 1L; 

    @Override 
    public Object findFilterId(Annotated a) { 
     Object result = super.findFilterId(a); 
     if (null != result) return result; 

     // By always returning a value, we cause Jackson to query the filter provider. 
     // A more sophisticated solution will introspect the annotated class and only 
     // return a value if the class contains annotated properties. 
     return a instanceof AnnotatedClass ? VisibilityFilterProvider.FILTER_ID : null; 
    } 
} 

這基本上是一個副本SimpleBeanProvider替換調用include通過調用isVisible。我可能會更新它以使用Java 8 BiPredicate使解決方案更通用但現在可用。 該類還將另一個過濾器作爲參數,並將委託給它以確定是否在字段可見時序列化字段的最終決定。

public class AuthorizationFilter extends SimpleBeanPropertyFilter { 
    private final PropertyFilter antecedent; 

    public AuthorizationFilter() { 
     this(null); 
    } 

    public AuthorizationFilter(final PropertyFilter filter) { 
     this.antecedent = null != filter ? filter : serializeAll(); 
    } 

    @Deprecated 
    @Override 
    public void serializeAsField(Object bean, JsonGenerator jgen, SerializerProvider provider, BeanPropertyWriter writer) throws Exception { 
     if (isVisible(bean, writer)) { 
      this.antecedent.serializeAsField(bean, jgen, provider, writer); 
     } else if (!jgen.canOmitFields()) { // since 2.3 
      writer.serializeAsOmittedField(bean, jgen, provider); 
     } 
    } 

    @Override 
    public void serializeAsField(Object pojo, JsonGenerator jgen, SerializerProvider provider, PropertyWriter writer) throws Exception { 
     if (isVisible(pojo, writer)) { 
      this.antecedent.serializeAsField(pojo, jgen, provider, writer); 
     } else if (!jgen.canOmitFields()) { // since 2.3 
      writer.serializeAsOmittedField(pojo, jgen, provider); 
     } 
    } 

    @Override 
    public void serializeAsElement(Object elementValue, JsonGenerator jgen, SerializerProvider provider, PropertyWriter writer) throws Exception { 
     if (isVisible(elementValue, writer)) { 
      this.antecedent.serializeAsElement(elementValue, jgen, provider, writer); 
     } 
    } 

    private static boolean isVisible(Object pojo, PropertyWriter writer) { 
     // Code to determine if the field should be serialized. 
    } 

} 

然後,我爲每個ObjectMapper實例添加一個自定義過濾器提供程序。

@SuppressWarnings("deprecation") 
public class VisibilityFilterProvider extends SimpleFilterProvider { 
    private static final long serialVersionUID = 1L; 
    static final String FILTER_ID = "dummy-filter-id"; 

    @Override 
    public BeanPropertyFilter findFilter(Object filterId) { 
     return super.findFilter(filterId); 
    } 

    @Override 
    public PropertyFilter findPropertyFilter(Object filterId, Object valueToFilter) { 
     if (FILTER_ID.equals(filterId)) { 
      // This implies that the class did not have an explict filter annotation. 
      return new AuthorizationFilter(null); 
     } 

     // The class has an explicit filter annotation so delegate to it. 
     final PropertyFilter antecedent = super.findPropertyFilter(filterId, valueToFilter); 
     return new VisibilityPropertyFilter(antecedent); 
    } 
} 

最後,我有一個傑克遜模塊,這樣我就不必手動將其添加到每個ObjectMapper實例自動註冊自定義annotaion內省。

public class FieldVisibilityModule extends SimpleModule { 
    private static final long serialVersionUID = 1L; 

    public FieldVisibilityModule() { 
     super(PackageVersion.VERSION); 
    } 

    @Override 
    public void setupModule(Module.SetupContext context) { 
     super.setupModule(context); 
     // Append after other introspectors (instead of before) since 
     // explicit annotations should have precedence 
     context.appendAnnotationIntrospector(new VisibilityAnnotationIntrospector()); 
    } 
} 

有更多改進,可以製備和我仍然有更多的單元測試寫入(例如,處理數組和集合),但,這是我所使用的基本策略。

相關問題