Gson:如何从没有注释的序列化中排除特定的字段

我正在努力学习Gson,而我正在努力解决现场排斥问题。 这是我的课程

public class Student { private Long id; private String firstName = "Philip"; private String middleName = "J."; private String initials = "PF"; private String lastName = "Fry"; private Country country; private Country countryOfBirth; } public class Country { private Long id; private String name; private Object other; } 

我可以使用GsonBuilder并添加ExclusionStrategy像字段名称像firstNamecountry但我似乎无法设法排除像country.name这样的特定字段的属性。

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

我将不胜感激这个问题的解决方案的任何帮助。

PS:我想避免注释,因为我想改善这一点,并使用正则表达式来过滤字段。

谢谢

编辑 :我想看看是否有可能模仿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 。 我也不想把国家排除在外。 所以类型是相同的,它是我想要查明和排除的对象图中的实际位置。

任何你不想序列化的字段通常应该使用“transient”修饰符,这也适用于json序列化器(至少它包含了我已经使用的一些,包括gson)。

如果你不想在序列化的json中显示名字,给它一个瞬态关键字,例如:

 private transient String name; 

Gson文档中的更多细节

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

 @Expose private Long id; 

不要任何你不想序列化的字段。 然后用这种方法创建你的Gson对象:

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

所以,你想排除 firstNamecountry.name 。 这是您的ExclusionStrategy应该看起来像什么

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

如果您仔细查看,则返回true ,作为您想要排除的Student.firstNameCountry.name

你需要像这样应用这个ExclusionStrategy

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

这返回:

{ “中间名”: “J”, “缩写”: “PF”, “姓氏”: “油炸”, “国家”:{ “ID”:91}}

我假设国家对象在学生类中用id = 91L初始化。


你可能会喜欢。 例如,你不想序列化名称中包含“name”字符串的任何字段。 做这个

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

这将返回

{ “的缩写”: “PF”, “国”:{ “ID”:91}}


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

ExclusionStrategy将做的事情,但你需要通过“完全合格的字段名称”。 见下文:

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

它返回

{ “名字”: “菲利普”, “中间名”: “学家”, “缩写”: “PF”, “姓氏”: “油炸”, “国家”:{ “ID”:91}}

读完所有可用的答案后,我发现最灵活的就是使用自定义@Exclude注释。 所以,我为此实现了简单的策略(我不想用@Expose来标记所有的字段,也不想用与应用程序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 ,除非我明确使用@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可以更简明地完成(这对于这些自定义排除策略仍然有效)。

你可以用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); } 

或者可以说什么领域不会暴露:

 Gson gson = gsonBuilder.excludeFieldsWithModifiers(Modifier.TRANSIENT).create(); 

在你的课上属性:

 private **transient** boolean nameAttribute; 

我用自定义注释解决了这个问题。 这是我的“SkipSerialisation”注释类:

 @Target (ElementType.FIELD) public @interface SkipSerialisation { } 

这是我的GsonBuilder:

 gsonBuilder.addSerializationExclusionStrategy(new ExclusionStrategy() { @Override public boolean shouldSkipField (FieldAttributes f) { return f.getAnnotation(SkipSerialisation.class) != null; } @Override public boolean shouldSkipClass (Class<?> clazz) { return false; } }); 

例如:

 public class User implements Serializable { public String firstName; public String lastName; @SkipSerialisation public String email; } 

另一种方法(特别有用的,如果你需要决定在运行时排除一个字段)是用你的gson实例注册一个TypeAdapter。 示例如下:

 Gson gson = new GsonBuilder() .registerTypeAdapter(BloodPressurePost.class, new BloodPressurePostSerializer()) 

在下面的情况下,服务器将期望两个值之一,但既然他们都是ints然后gson会序列化他们两个。 我的目标是从发布到服务器的json中省略零(或更少)的任何值。

 public class BloodPressurePostSerializer implements JsonSerializer<BloodPressurePost> { @Override public JsonElement serialize(BloodPressurePost src, Type typeOfSrc, JsonSerializationContext context) { final JsonObject jsonObject = new JsonObject(); if (src.systolic > 0) { jsonObject.addProperty("systolic", src.systolic); } if (src.diastolic > 0) { jsonObject.addProperty("diastolic", src.diastolic); } jsonObject.addProperty("units", src.units); return jsonObject; } } 

我使用这个策略:我排除了所有没有@SerializedName注解标记的字段,即:

 public class Dummy { @SerializedName("VisibleValue") final String visibleValue; final String hiddenValue; public Dummy(String visibleValue, String hiddenValue) { this.visibleValue = visibleValue; this.hiddenValue = hiddenValue; } } public class SerializedNameOnlyStrategy implements ExclusionStrategy { @Override public boolean shouldSkipField(FieldAttributes f) { return f.getAnnotation(SerializedName.class) == null; } @Override public boolean shouldSkipClass(Class<?> clazz) { return false; } } Gson gson = new GsonBuilder() .setExclusionStrategies(new SerializedNameOnlyStrategy()) .create(); Dummy dummy = new Dummy("I will see this","I will not see this"); String json = gson.toJson(dummy); 

它返回

{“VisibleValue”:“我会看到这个”}

我只是把@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); }