【问题标题】:Jackson filtering out fields without annotations杰克逊过滤掉没有注释的字段
【发布时间】:2017-06-21 08:50:03
【问题描述】:

我试图通过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 注释的答案。

【问题讨论】:

  • 嗨,JMax,我的回答有帮助吗?
  • 嗨,JMax。我注意到您已在原始问题中添加了注释,因此我相应地更改了答案,请检查编辑。

标签: java json jackson


【解决方案1】:

如果由于某种原因 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);
    }
});

【讨论】:

  • 正是我想要的,+1
【解决方案2】:

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

@JsonFilter("test")
class Data {

您已指定不能在类上使用注释。您可以使用 mix-ins 来避免注释 Data 类。

@JsonFilter("test")
class DataMixIn {}

ObjectMapper 上指定混合have to,并且您指定不想重新配置它。在这种情况下,您可以随时复制ObjectMapper 及其配置,然后修改副本的配置。这不会影响代码中其他地方使用的原始ObjectMapper。例如

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

【讨论】:

  • 完美运行。谢谢。在过滤从 couchbase 获取的数据时使用此功能,而插入数据时无需过滤
【解决方案3】:

按名称排除属性示例:

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 的属性,您可以将方法 addMixIn 的参数 Object.class 更改为 User.class

按类型排除属性,您可以创建MixInByType

@JsonIgnoreType
public class MixInByType {
}

ObjectMapper mapper = new ObjectMapper()
      .addMixIn(Integer.class, MixInByType.class);

mapper.writeValueAsString(user); // {"name":"abc"}

【讨论】:

  • 该解决方案对我有用,除了我使用“SimpleBeanPropertyFilter.serializeAllExcept”来排除不需要的属性,而不是“SimpleBeanPropertyFilter.filterOutAllExcept”,其中包括除指定之外的所有属性。
【解决方案4】:

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

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

编辑

OP刚刚添加了一个注释,只是回答不使用bean动画,然后如果您要导出的字段数量非常少,您可以检索该数据并自己构建一个列表地图,那里似乎没有其他方法可以做到这一点。

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 类上的版本控制注释很容易满足您的需求,如下所示:

@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 在性能和易用性方面都非常出色。

【讨论】:

  • 很抱歉,无法更改为 Gson,我的大型应用程序基于 Jackson,更改此设置需要数周时间。
  • 另外我写道,我不能使用注释,而您的答案基于要忽略的字段上的注释。
  • 顺便说一下,我不建议在另一个类文件中使用静态类,除非真的需要,即使是测试的东西:D
猜你喜欢
  • 1970-01-01
  • 2015-05-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-25
  • 1970-01-01
相关资源
最近更新 更多