我在努力学习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排除在外。 所以类型是一样的我想要精确定位和排除的对象图中的实际位置。


当前回答

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

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);
}

其他回答

我有Kotlin版本

@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FIELD)
internal annotation class JsonSkip

class SkipFieldsStrategy : ExclusionStrategy {

    override fun shouldSkipClass(clazz: Class<*>): Boolean {
        return false
    }

    override fun shouldSkipField(f: FieldAttributes): Boolean {
        return f.getAnnotation(JsonSkip::class.java) != null
    }
}

以及如何将其添加到Retrofit GSONConverterFactory:

val gson = GsonBuilder()
                .setExclusionStrategies(SkipFieldsStrategy())
                //.serializeNulls()
                //.setDateFormat(DateFormat.LONG)
                //.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)
                //.setPrettyPrinting()
                //.registerTypeAdapter(Id.class, IdTypeAdapter())
                .create()
        return GsonConverterFactory.create(gson)

我只是把@Expose注释放在这里,这里是我使用的版本

compile 'com.squareup.retrofit2:retrofit:2.0.2'
compile 'com.squareup.retrofit2:converter-gson:2.0.2'

在Model类中:

@Expose
int number;

public class AdapterRestApi {

在Adapter类中:

public EndPointsApi connectRestApi() {
    OkHttpClient client = new OkHttpClient.Builder()
            .connectTimeout(90000, TimeUnit.SECONDS)
            .readTimeout(90000,TimeUnit.SECONDS).build();

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(ConstantRestApi.ROOT_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .client(client)
            .build();

    return retrofit.create  (EndPointsApi.class);
}

Nishant提供了一个很好的解决方案,但还有更简单的方法。简单地用@Expose注释标记所需的字段,例如:

@Expose private Long id;

省略您不想序列化的任何字段。然后按如下方式创建你的Gson对象:

Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();

为缓存对象使用不同的DTO。

例如,您可以创建UserCached类并只保留您需要的字段。 之后,创建映射器来来回映射对象。Mapstruct在这方面做得很好。

这种方法解决了问题,解耦了应用程序,并使主DTO中的更改更加安全。

在阅读了所有可用的答案后,我发现,对我来说,最灵活的方法是使用自定义的@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();