我在努力学习Gson,我在与场排除作斗争。这是我的课程

public class Student {    
  private Long                id;
  private String              firstName        = "Philip";
  private String              middleName       = "J.";
  private String              initials         = "P.F";
  private String              lastName         = "Fry";
  private Country             country;
  private Country             countryOfBirth;
}

public class Country {    
  private Long                id;
  private String              name;
  private Object              other;
}

我可以使用GsonBuilder,并为字段名如firstName或country添加一个ExclusionStrategy,但我似乎无法排除某些字段的属性,如country.name。

使用方法public boolean shouldSkipField(FieldAttributes fa), FieldAttributes不包含足够的信息来匹配像country.name这样的过滤器。

附注:我想避免使用注释,因为我想改进这一点,并使用RegEx过滤字段。

编辑:我试着看看是否有可能模拟Struts2 JSON插件的行为

使用Gson

<interceptor-ref name="json">
  <param name="enableSMD">true</param>
  <param name="excludeProperties">
    login.password,
    studentList.*\.sin
  </param>
</interceptor-ref>

编辑: 我重新提出这个问题,补充如下:

我添加了第二个具有相同类型的字段,以进一步澄清这个问题。基本上我想要排除country。name而不是countrofbirth。name。我也不想把Country排除在外。 所以类型是一样的我想要精确定位和排除的对象图中的实际位置。


当前回答

在阅读了所有可用的答案后,我发现,对我来说,最灵活的方法是使用自定义的@Exclude注释。所以,我为此实现了简单的策略(我不想使用@Expose标记所有字段,也不想使用与app Serializable序列化冲突的transient):

注释:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Exclude {
}

策略:

public class AnnotationExclusionStrategy implements ExclusionStrategy {

    @Override
    public boolean shouldSkipField(FieldAttributes f) {
        return f.getAnnotation(Exclude.class) != null;
    }

    @Override
    public boolean shouldSkipClass(Class<?> clazz) {
        return false;
    }
}

用法:

new GsonBuilder().setExclusionStrategies(new AnnotationExclusionStrategy()).create();

其他回答

因此,你想要排除firstName和country。name。以下是你的排除策略应该是什么样的

    public class TestExclStrat implements ExclusionStrategy {

        public boolean shouldSkipClass(Class<?> arg0) {
            return false;
        }

        public boolean shouldSkipField(FieldAttributes f) {

            return (f.getDeclaringClass() == Student.class && f.getName().equals("firstName"))||
            (f.getDeclaringClass() == Country.class && f.getName().equals("name"));
        }

    }

仔细看,Student返回true。firstName和country。name,这是你想要排除的。

你需要像这样应用排他策略,

    Gson gson = new GsonBuilder()
        .setExclusionStrategies(new TestExclStrat())
        //.serializeNulls() <-- uncomment to serialize NULL fields as well
        .create();
    Student src = new Student();
    String json = gson.toJson(src);
    System.out.println(json);

这将返回:

{ "middleName": "J.", "initials": "P.F", "lastName": "Fry", "country": { "id": 91}}

我假设country对象在student类中用id = 91L初始化。


你可能会幻想。例如,您不希望序列化名称中包含“name”字符串的任何字段。这样做:

public boolean shouldSkipField(FieldAttributes f) {
    return f.getName().toLowerCase().contains("name"); 
}

这将返回:

{ "initials": "P.F", "country": { "id": 91 }}

编辑:按要求添加了更多信息。

这个排他策略会做这件事,但你需要传递“完全限定字段名”。见下文:

    public class TestExclStrat implements ExclusionStrategy {

        private Class<?> c;
        private String fieldName;
        public TestExclStrat(String fqfn) throws SecurityException, NoSuchFieldException, ClassNotFoundException
        {
            this.c = Class.forName(fqfn.substring(0, fqfn.lastIndexOf(".")));
            this.fieldName = fqfn.substring(fqfn.lastIndexOf(".")+1);
        }
        public boolean shouldSkipClass(Class<?> arg0) {
            return false;
        }

        public boolean shouldSkipField(FieldAttributes f) {

            return (f.getDeclaringClass() == c && f.getName().equals(fieldName));
        }

    }

下面是我们如何一般地使用它。

    Gson gson = new GsonBuilder()
        .setExclusionStrategies(new TestExclStrat("in.naishe.test.Country.name"))
        //.serializeNulls()
        .create();
    Student src = new Student();
    String json = gson.toJson(src);
    System.out.println(json); 

它返回:

{ "firstName": "Philip" , "middleName": "J.", "initials": "P.F", "lastName": "Fry", "country": { "id": 91 }}

你可以用gson来查看json树。

试试这样做:

gson.toJsonTree(student).getAsJsonObject()
.get("country").getAsJsonObject().remove("name");

你也可以添加一些属性:

gson.toJsonTree(student).getAsJsonObject().addProperty("isGoodStudent", false);

用gson 2.2.4测试。

我提出了一个类工厂来支持这个功能。传入想要排除的字段或类的任何组合。

public class GsonFactory {

    public static Gson build(final List<String> fieldExclusions, final List<Class<?>> classExclusions) {
        GsonBuilder b = new GsonBuilder();
        b.addSerializationExclusionStrategy(new ExclusionStrategy() {
            @Override
            public boolean shouldSkipField(FieldAttributes f) {
                return fieldExclusions == null ? false : fieldExclusions.contains(f.getName());
            }

            @Override
            public boolean shouldSkipClass(Class<?> clazz) {
                return classExclusions == null ? false : classExclusions.contains(clazz);
            }
        });
        return b.create();

    }
}

为了使用,创建两个列表(每个列表都是可选的),并创建你的GSON对象:

static {
 List<String> fieldExclusions = new ArrayList<String>();
 fieldExclusions.add("id");
 fieldExclusions.add("provider");
 fieldExclusions.add("products");

 List<Class<?>> classExclusions = new ArrayList<Class<?>>();
 classExclusions.add(Product.class);
 GSON = GsonFactory.build(null, classExclusions);
}

private static final Gson GSON;

public String getSomeJson(){
    List<Provider> list = getEntitiesFromDatabase();
    return GSON.toJson(list);
}

在阅读了所有可用的答案后,我发现,对我来说,最灵活的方法是使用自定义的@Exclude注释。所以,我为此实现了简单的策略(我不想使用@Expose标记所有字段,也不想使用与app Serializable序列化冲突的transient):

注释:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Exclude {
}

策略:

public class AnnotationExclusionStrategy implements ExclusionStrategy {

    @Override
    public boolean shouldSkipField(FieldAttributes f) {
        return f.getAnnotation(Exclude.class) != null;
    }

    @Override
    public boolean shouldSkipClass(Class<?> clazz) {
        return false;
    }
}

用法:

new GsonBuilder().setExclusionStrategies(new AnnotationExclusionStrategy()).create();

我遇到了这个问题,在这个问题中,我只想从序列化中排除少量字段,所以我开发了一个相当简单的解决方案,使用带有自定义排除策略的Gson的@Expose注释。

使用@Expose的唯一内置方法是设置GsonBuilder.excludeFieldsWithoutExposeAnnotation(),但正如名称所示,没有显式@Expose的字段将被忽略。由于我只有几个想要排除的字段,我发现将注释添加到每个字段非常麻烦。

实际上,我想要的是相反的,其中所有内容都包括在内,除非我显式地使用@Expose来排除它。我使用了以下排除策略来实现这一点:

new GsonBuilder()
        .addSerializationExclusionStrategy(new ExclusionStrategy() {
            @Override
            public boolean shouldSkipField(FieldAttributes fieldAttributes) {
                final Expose expose = fieldAttributes.getAnnotation(Expose.class);
                return expose != null && !expose.serialize();
            }

            @Override
            public boolean shouldSkipClass(Class<?> aClass) {
                return false;
            }
        })
        .addDeserializationExclusionStrategy(new ExclusionStrategy() {
            @Override
            public boolean shouldSkipField(FieldAttributes fieldAttributes) {
                final Expose expose = fieldAttributes.getAnnotation(Expose.class);
                return expose != null && !expose.deserialize();
            }

            @Override
            public boolean shouldSkipClass(Class<?> aClass) {
                return false;
            }
        })
        .create();

现在,我可以很容易地用@Expose(serialize = false)或@Expose(deserialize = false)注释排除一些字段(注意,这两个@Expose属性的默认值都是true)。当然,您可以使用@Expose(serialize = false, deserialize = false),但是通过声明字段transient(使用这些自定义排除策略仍然有效)可以更简洁地实现这一点。