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像字段名称像firstName
或country
但我似乎无法设法排除像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();
所以,你想排除 firstName
和country.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.firstName
和Country.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); }