杰克逊过滤掉没有注释的字段

时间:2017-06-21 08:50:04

标签: java json jackson

我尝试使用以下(简化)代码通过SimpleBeanPropertyFilter过滤掉序列化中的某些字段:

public static void main(String[] args) {
    ObjectMapper mapper = new ObjectMapper();

    SimpleFilterProvider filterProvider = new SimpleFilterProvider().addFilter("test",
            SimpleBeanPropertyFilter.filterOutAllExcept("data1"));
    try {
        String json = mapper.writer(filterProvider).writeValueAsString(new Data());

        System.out.println(json); // output: {"data1":"value1","data2":"value2"}

    } catch (JsonProcessingException e) {
        e.printStackTrace();
    }
}

private static class Data {
    public String data1 = "value1";
    public String data2 = "value2";
}

我使用SimpleBeanPropertyFilter.filterOutAllExcept("data1"));我期望创建的序列化Json字符串仅包含{"data1":"value1"},但我得到{"data1":"value1","data2":"value2"}

如何创建一个尊重指定过滤器的临时编写器(在我的情况下无法重新配置ObjectMapper)。

注意:由于我的应用程序中的使用场景,我只能接受不使用Jackson注释的答案。

4 个答案:

答案 0 :(得分:6)

您通常会注释Data类以应用过滤器:

@JsonFilter("test")
class Data {

您已指定不能在课程上使用注释。您可以使用混合来避免注释Data类。

@JsonFilter("test")
class DataMixIn {}

ObjectMapper上指定了Mixins have to,并指定您不想重新配置它。在这种情况下,您始终可以使用其配置复制ObjectMapper,然后修改副本的配置。这不会影响代码中其他地方使用的原始ObjectMapper。 E.g。

ObjectMapper myMapper = mapper.copy();
myMapper.addMixIn(Data.class, DataMixIn.class);

然后使用新的ObjectMapper

进行书写
String json = myMapper.writer(filterProvider).writeValueAsString(new Data());
System.out.println(json); // output: {"data1":"value1"}

答案 1 :(得分:6)

按名称排除属性的示例:

$ curl "localhost:9200/dt/art/_search?pretty" -d'{
    "query": {
      "bool": { "must": { "match_all": {} },
                "filter": {
                  "range": {
                  "age": { "gte": 20, "lte": 50}}}}}}'


{ "took" : 8, "timed_out" : false,
  "_shards" : { "total" : 5, "successful" : 5, "skipped" : 0, "failed" : 0 },
  "hits" : {
    "total" : 2,
    "max_score" : 1.0,
    "hits" : [
      { "_index" : "dt", "_type" : "art", "_id" : "4", "_score" : 1.0,
        "_source" : { "age" : 33 } },
      { "_index" : "dt", "_type" : "art", "_id" : "3", "_score" : 1.0,
        "_source" : { "age" : 42 } }
    ]
  }
}
$ 

您可以代替public Class User { private String name = "abc"; private Integer age = 1; //getters } @JsonFilter("dynamicFilter") public class DynamicMixIn { } User user = new User(); String[] propertiesToExclude = {"name"}; ObjectMapper mapper = new ObjectMapper() .addMixIn(Object.class, DynamicMixIn.class); FilterProvider filterProvider = new SimpleFilterProvider() .addFilter("dynamicFilter", SimpleBeanPropertyFilter.filterOutAllExcept(propertiesToExclude)); mapper.setFilterProvider(filterProvider); mapper.writeValueAsString(user); // {"name":"abc"} 创建DynamicMixIn

MixInByPropName

注意:如果您只想为@JsonIgnoreProperties(value = {"age"}) public class MixInByPropName { } ObjectMapper mapper = new ObjectMapper() .addMixIn(Object.class, MixInByPropName.class); mapper.writeValueAsString(user); // {"name":"abc"} 排除属性,可以将方法User的参数Object.class更改为addMixIn

按类型排除属性,您可以创建User.class

MixInByType

答案 2 :(得分:3)

如果由于某种原因,MixIns不适合你。您可以尝试这种方法:

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setAnnotationIntrospector(new JacksonAnnotationIntrospector(){
    @Override
    public boolean hasIgnoreMarker(final AnnotatedMember m) {

    List<String> exclusions = Arrays.asList("field1", "field2");
    return exclusions.contains(m.getName())|| super.hasIgnoreMarker(m);
    }
});

答案 3 :(得分:0)

如果你想让过滤器工作,你似乎必须添加一个注释来指示在对bean类进行序列化时使用哪个过滤器:

@JsonFilter("test")
public class Data {
    public String data1 = "value1";
    public String data2 = "value2";
}

修改

OP刚刚添加了一个注释,只是得到了不使用bean动画的答案,那么如果要导出的字段数量非常少,那么您可以自己检索该数据并构建List of List,似乎没有办法做到这一点。

Map<String, Object> map = new HashMap<String, Object>();
map.put("data1", obj.getData1());
...
// do the serilization on the map object just created.

如果您想要排除特定字段并保留最多字段,也许您可​​以通过反射来实现。以下是我编写的将bean传输到地图的方法,您可以更改代码以满足自己的需求:

protected Map<String, Object> transBean2Map(Object beanObj){
        if(beanObj == null){
            return null;
        }
        Map<String, Object> map = new HashMap<String, Object>();

        try {
            BeanInfo beanInfo = Introspector.getBeanInfo(beanObj.getClass());
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor property : propertyDescriptors) {
                String key = property.getName();


                if (!key.equals("class")
                        && !key.endsWith("Entity")
                        && !key.endsWith("Entities")
                        && !key.endsWith("LazyInitializer")
                        && !key.equals("handler")) {


                    Method getter = property.getReadMethod();

                    if(key.endsWith("List")){
                        Annotation[] annotations = getter.getAnnotations();
                        for(Annotation annotation : annotations){
                            if(annotation instanceof javax.persistence.OneToMany){
                                if(((javax.persistence.OneToMany)annotation).fetch().equals(FetchType.EAGER)){
                                    List entityList = (List) getter.invoke(beanObj);
                                    List<Map<String, Object>> dataList = new ArrayList<>();
                                    for(Object childEntity: entityList){
                                        dataList.add(transBean2Map(childEntity));
                                    }
                                    map.put(key,dataList);
                                }
                            }
                        }
                        continue;
                    }

                    Object value = getter.invoke(beanObj);

                    map.put(key, value);
                }
            }
        } catch (Exception e) {
            Logger.getAnonymousLogger().log(Level.SEVERE,"transBean2Map Error " + e);
        }
        return map;
    }

但我建议您使用Google Gson作为JSON反序列化器/序列化器。主要原因是我讨厌处理异常内容,它只是搞乱了编码风格。

通过像bean这样利用bean类的版本控制注释,很容易满足你的需求:

@Since(GifMiaoMacro.GSON_SENSITIVE) //mark the field as sensitive data and will not export to JSON
private boolean firstFrameStored; // won't export this field to JSON.

您可以定义宏是否导出或隐藏字段,如下所示:

 public static final double GSON_SENSITIVE = 2.0f;
 public static final double GSON_INSENSITIVE = 1.0f;

默认情况下,Gson会导出所有未注明@Since的字段。因此,如果您不关心该字段,则不必执行任何操作,只需导出该字段。

如果某些字段您不想导出到json,即敏感信息只是在字段中添加注释。并用这个生成json字符串:

 private static Gson gsonInsensitive = new GsonBuilder()
            .registerTypeAdapter(ObjectId.class,new ObjectIdSerializer()) // you can omit this line and the following line if you are not using mongodb
            .registerTypeAdapter(ObjectId.class, new ObjectIdDeserializer()) //you can omit this
            .setVersion(GifMiaoMacro.GSON_INSENSITIVE)
            .disableHtmlEscaping()
            .create();

public static String toInsensitiveJson(Object o){
    return gsonInsensitive.toJson(o);
}

然后就这样使用:

 String jsonStr = StringUtils.toInsensitiveJson(yourObj);

由于Gson是无状态的,使用静态方法完成你的工作很好,我用Java尝试了很多JSON序列化/反序列化框架,但发现Gson既性能又方便