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

Liv*_* T. 404 java serialization json gson

我正在努力学习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;
}
Run Code Online (Sandbox Code Playgroud)

我可以使用GsonBuilder并添加ExclusionStrategy像一个字段名称firstNamecountry,但我似乎无法管理排除像某些领域的性能country.name.

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

对于这个问题的解决方案,我将不胜感激.

PS:我想避免注释,因为我想对此进行改进并使用RegEx来过滤字段.

谢谢

编辑:我试图看看是否可以模拟Struts2 JSON插件的行为

使用Gson

<interceptor-ref name="json">
  <param name="enableSMD">true</param>
  <param name="excludeProperties">
    login.password,
    studentList.*\.sin
  </param>
</interceptor-ref>
Run Code Online (Sandbox Code Playgroud)

编辑: 我重新打开了以下添加的问题:

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

Chr*_*ine 615

您不希望序列化的任何字段都应该使用"transient"修饰符,这也适用于json序列化程序(至少它适用于我使用过的一些,包括gson).

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

private transient String name;
Run Code Online (Sandbox Code Playgroud)

Gson文档中的更多细节

  • 需要注意的一点是瞬态影响序列化和反序列化.它还会将已序列化的值发送到对象中,即使它存在于JSON中也是如此. (34认同)
  • 这种方法对我不起作用,因为它不仅从gson序列化中排除了该字段,而且从内部应用程序序列化中排除了该字段(使用Serializable接口). (11认同)
  • 瞬态阻止了场的SERIALIZATION和DESERIALIZATION.这与我的需求不符. (8认同)
  • 它与排除注释几乎相同,因为它适用于该类的所有实例.我想要运行时动态排除.在某些情况下,我希望排除某些字段以提供较轻/受限制的响应,而在其他情况下,我希望将完整对象序列化 (4认同)
  • 使用`transient`而不是`@Expose`的问题在于你仍然需要在你的客户端上使用可能进入的所有字段来模拟POJO.对于可能在项目之间共享的后端API ,如果添加其他字段,这可能会有问题.基本上它是白名单与黑名单领域. (3认同)
  • 当字段必须保留在数据库中时,它将不起作用。例如,休眠也会忽略瞬态字段。 (2认同)
  • 如果要将该字段保存在数据库中怎么办?该解决方案将中断 (2认同)

Jay*_*Pea 312

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

@Expose private Long id;
Run Code Online (Sandbox Code Playgroud)

省去您不想序列化的任何字段.然后以这种方式创建您的Gson对象:

Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
Run Code Online (Sandbox Code Playgroud)

  • 是否有可能有"notExpose"之类的东西,并且只能忽略那些除了一个字段必须被序列化并且为所有字段添加注释多余的情况? (92认同)
  • @DaSh我最近有这样的场景.编写自定义的ExclusionStrategy非常容易.见Nishant的回答.唯一的问题是包含一堆容器类,并且使用skipclass vs skipfield(字段可以是类......) (2认同)
  • @DaSh [我的回答](http://stackoverflow.com/a/17733569/657111) 下面正是这样做的。 (2认同)
  • @Danlil你应该能够使用@Expose(serialize = false, deserialize = false) (2认同)

Nis*_*ant 228

所以,你要排除 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"));
        }

    }
Run Code Online (Sandbox Code Playgroud)

如果你仔细看它返回trueStudent.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);
Run Code Online (Sandbox Code Playgroud)

返回:

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

我假设country对象id = 91L在学生班中初始化.


你可能会喜欢.例如,您不希望序列化名称中包含"name"字符串的任何字段.做这个:

{ "middleName": "J.", "initials": "P.F", "lastName": "Fry", "country": { "id": 91}}
Run Code Online (Sandbox Code Playgroud)

这将返回:

public boolean shouldSkipField(FieldAttributes f) {
    return f.getName().toLowerCase().contains("name"); 
}
Run Code Online (Sandbox Code Playgroud)

编辑:根据要求添加了更多信息.

这样ExclusionStrategy做,但你需要通过"完全合格的字段名称".见下文:

{ "initials": "P.F", "country": { "id": 91 }}
Run Code Online (Sandbox Code Playgroud)

以下是我们如何一般地使用它.

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

    }
Run Code Online (Sandbox Code Playgroud)

它返回:

    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); 
Run Code Online (Sandbox Code Playgroud)

  • 这个答案应该标记为首选答案.与目前拥有更多选票的其他答案不同,此解决方案不要求您修改bean类.这是一个巨大的优势.如果其他人使用相同的bean类,并且你将他们想要的字段标记为"瞬态"怎么办? (11认同)

pkk*_*pkk 213

在阅读了我发现的所有可用答案之后,在我的情况下,最灵活的是使用自定义@Exclude注释.所以,我为此实现了简单的策略(我不想标记所有字段,@Expose也不想使用transient与应用程序Serializable序列化相冲突的字段):

注解:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Exclude {
}
Run Code Online (Sandbox Code Playgroud)

战略:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

new GsonBuilder().setExclusionStrategies(new AnnotationExclusionStrategy()).create();
Run Code Online (Sandbox Code Playgroud)

  • 另外需要注意的是,如果要将排除策略仅用于序列化或仅用于反序列化,请使用:`addSerializationExclusionStrategy`或`addDeserializationExclusionStrategy`而不是`setExclusionStrategies` (15认同)
  • 完善!瞬态解决方案对我不起作用,因为我正在使用Realm for DB而我想从Gson中排除一个字段,而不是Realm(具有瞬态功能) (9认同)
  • 这应该是公认的答案.要忽略空字段,只需将`f.getAnnotation(Exclude.class)!= null`更改为`f.getAnnotation(Exclude.class)== null` (3认同)
  • 由于其他图书馆的需要,你不能使用"瞬态",这是非常好的.谢谢! (3认同)

Der*_*key 78

我遇到了这个问题,其中我只想从序列化中排除少量字段,因此我开发了一个相当简单的解决方案,它使用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();
Run Code Online (Sandbox Code Playgroud)

现在我可以轻松地使用@Expose(serialize = false)@Expose(deserialize = false)注释排除一些字段(请注意,两个@Expose属性的默认值都是true).您当然可以使用@Expose(serialize = false, deserialize = false),但通过声明字段transient(这仍然适用于这些自定义排除策略)可以更简洁地完成.


小智 18

你可以用gson探索json树.

尝试这样的事情:

gson.toJsonTree(student).getAsJsonObject()
.get("country").getAsJsonObject().remove("name");
Run Code Online (Sandbox Code Playgroud)

您还可以添加一些属性:

gson.toJsonTree(student).getAsJsonObject().addProperty("isGoodStudent", false);
Run Code Online (Sandbox Code Playgroud)

用gson 2.2.4测试.

  • 我想知道如果你想摆脱一个必须在删除之前解析的复杂属性,这是否太过性能损失.思考? (2认同)

Dom*_* D. 16

我想出了一个类工厂来支持这个功能.传递要排除的字段或类的任意组合.

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

    }
}
Run Code Online (Sandbox Code Playgroud)

要使用,请创建两个列表(每个都是可选的),然后创建您的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);
}
Run Code Online (Sandbox Code Playgroud)


Hib*_*bem 13

我用自定义注释解决了这个问题.这是我的"SkipSerialisation"注释类:

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

}
Run Code Online (Sandbox Code Playgroud)

这是我的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;
  }
});
Run Code Online (Sandbox Code Playgroud)

示例:

public class User implements Serializable {

  public String firstName;

  public String lastName;

  @SkipSerialisation
  public String email;
}
Run Code Online (Sandbox Code Playgroud)

  • Gson:如何在没有注释的情况下从序列化**中排除特定字段** (5认同)
  • 您还应该在注释中添加`@Retention(RetentionPolicy.RUNTIME)`. (3认同)

loo*_*shc 10

Kotlin 的@Transient注释显然也起到了作用。

data class Json(
    @field:SerializedName("serialized_field_1") val field1: String,
    @field:SerializedName("serialized_field_2") val field2: String,
    @Transient val field3: String
)
Run Code Online (Sandbox Code Playgroud)

输出:

{"serialized_field_1":"VALUE1","serialized_field_2":"VALUE2"}
Run Code Online (Sandbox Code Playgroud)


luc*_*iel 9

或者可以说什么字段不会暴露:

Gson gson = gsonBuilder.excludeFieldsWithModifiers(Modifier.TRANSIENT).create();
Run Code Online (Sandbox Code Playgroud)

你的班级属性:

private **transient** boolean nameAttribute;
Run Code Online (Sandbox Code Playgroud)

  • 默认情况下排除瞬态和静态字段; 没有必要为此调用`excludeFieldsWithModifiers()`. (16认同)

小智 8

我使用了这个策略:我排除了没有@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);
Run Code Online (Sandbox Code Playgroud)

它回来了

{"VisibleValue":"我会看到这个"}


Dam*_*ian 6

另一种方法(如果您需要决定在运行时排除字段,尤其有用)是使用您的gson实例注册TypeAdapter.示例如下:

Gson gson = new GsonBuilder()
.registerTypeAdapter(BloodPressurePost.class, new BloodPressurePostSerializer())
Run Code Online (Sandbox Code Playgroud)

在下面的情况中,服务器会期望两个值中的一个,但由于它们都是int,因此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;
    }
}
Run Code Online (Sandbox Code Playgroud)