Gson: How to exclude specific fields from Serialization without annotations
我正在努力学习GSON,我也在努力排除外场干扰。这是我的课
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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并为诸如
使用方法
如果能帮我解决这个问题,我将不胜感激。
P.S:我想避免注释,因为我想改进这一点,并使用regex过滤掉字段。
谢谢你
编辑:我想看看是否可以模拟struts2 JSON插件的行为。
使用GSON
1 2 3 4 5 6 7 | <interceptor-ref name="json"> <param name="enableSMD">true</param> <param name="excludeProperties"> login.password, studentList.*\.sin </param> </interceptor-ref> |
号
编辑:我重新打开问题并添加了以下内容:
为了进一步澄清这个问题,我添加了第二个相同类型的字段。基本上我想排除
一般来说,任何不希望序列化的字段都应该使用"transient"修饰符,这也适用于JSON序列化程序(至少它适用于我使用过的一些字段,包括GSON)。
如果不希望名称出现在序列化JSON中,请给它一个临时关键字,例如:
1 |
GSON文档中的更多详细信息
Nishant提供了一个很好的解决方案,但有一个更简单的方法。只需使用@expose注释标记所需字段,例如:
1 |
。
删除任何不想序列化的字段。然后用这种方式创建GSON对象:
1 | Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create(); |
所以,您要排除
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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")); } } |
如果你仔细观察,它会返回
你需要像这样应用这个
1 2 3 4 5 6 7 |
号
这将返回:
{"middleName":"J.","initials":"P.F","lastName":"Fry","country":{"id":91}}
号
我假设Country对象是在学生班中用
你可能会喜欢上。例如,您不希望序列化名称中包含"name"字符串的任何字段。执行此操作:
1 2 3 | public boolean shouldSkipField(FieldAttributes f) { return f.getName().toLowerCase().contains("name"); } |
这将返回:
1 | {"initials":"P.F","country": {"id": 91 }} |
。
编辑:根据需要添加更多信息。
这个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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)); } } |
下面是我们如何通用地使用它。
1 2 3 4 5 6 7 |
。
它返回:
1 | {"firstName":"Philip" ,"middleName":"J.","initials":"P.F","lastName":"Fry","country": {"id": 91 }} |
。
在阅读了所有可用的答案之后,我发现,在我的例子中,最灵活的方法是使用自定义的
注释:
1 2 3 4 | @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Exclude { } |
号
策略:
1 2 3 4 5 6 7 8 9 10 11 12 | 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; } } |
号
用途:
1 | new GsonBuilder().setExclusionStrategies(new AnnotationExclusionStrategy()).create(); |
号
我遇到了这个问题,在这个问题中,我只想从序列化中排除少量字段,因此我开发了一个相当简单的解决方案,它使用GSON的
使用
我实际上想要的是相反的,除非我明确地使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 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(); |
号
现在我可以很容易地排除一些带有
您可以使用gson探索json树。
尝试如下操作:
1 2 | gson.toJsonTree(student).getAsJsonObject() .get("country").getAsJsonObject().remove("name"); |
号
还可以添加一些属性:
1 | gson.toJsonTree(student).getAsJsonObject().addProperty("isGoodStudent", false); |
号
用GSON 2.2.4测试。
我想出了一个类工厂来支持这个功能。传递要排除的字段或类的任意组合。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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); } |
号
我用自定义注释解决了这个问题。这是我的"Skipserialisation"注释类:
1 2 3 4 | @Target (ElementType.FIELD) public @interface SkipSerialisation { } |
这是我的GSONBuilder:
1 2 3 4 5 6 7 8 9 10 11 12 13 | gsonBuilder.addSerializationExclusionStrategy(new ExclusionStrategy() { @Override public boolean shouldSkipField (FieldAttributes f) { return f.getAnnotation(SkipSerialisation.class) != null; } @Override public boolean shouldSkipClass (Class<?> clazz) { return false; } }); |
号
例子:
1 2 3 4 5 6 7 8 9 | public class User implements Serializable { public String firstName; public String lastName; @SkipSerialisation public String email; } |
或者可以说哪些字段将不会显示:
1 |
号
在类的属性上:
1 | private **transient** boolean nameAttribute; |
号
我使用了这个策略:我排除了没有用@serializedname注释标记的每个字段,即:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | 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":"I will see this"}
号
另一种方法(如果需要在运行时决定排除字段,则特别有用)是向GSON实例注册一个类型适配器。示例如下:
1 2 | Gson gson = new GsonBuilder() .registerTypeAdapter(BloodPressurePost.class, new BloodPressurePostSerializer()) |
在下面的例子中,服务器期望两个值中的一个,但是由于它们都是int,所以gson将对它们进行序列化。我的目标是从发布到服务器的JSON中省略零(或更小)的任何值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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; } } |
。
Kotlin的
1 2 3 4 5 |
。
输出:
1 | {"serialized_field_1":"VALUE1","serialized_field_2":"VALUE2"} |
我有Kotlin版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | @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 } } |
。
以及如何将其添加到改造GSOnConverterFactory中:
1 2 3 4 5 6 7 8 9 | val gson = GsonBuilder() .setExclusionStrategies(SkipFieldsStrategy()) //.serializeNulls() //.setDateFormat(DateFormat.LONG) //.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE) //.setPrettyPrinting() //.registerTypeAdapter(Id.class, IdTypeAdapter()) .create() return GsonConverterFactory.create(gson) |
我只是把
1 2 | compile 'com.squareup.retrofit2:retrofit:2.0.2' compile 'com.squareup.retrofit2:converter-gson:2.0.2' |
。
在
1 2 3 4 | @Expose int number; public class AdapterRestApi { |
在
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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); } |
。
这就是我一直使用的:
GSON中实现的默认行为是忽略空对象字段。
意味着gson对象不将具有空值的字段序列化为json。如果Java对象中的字段为NULL,则Gson将其排除。
您可以使用此函数将某些对象转换为空值或由您自己设置的良好值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | /** * convert object to json */ public String toJson(Object obj) { // Convert emtpy string and objects to null so we don't serialze them setEmtpyStringsAndObjectsToNull(obj); return gson.toJson(obj); } /** * Sets all empty strings and objects (all fields null) including sets to null. * * @param obj any object */ public void setEmtpyStringsAndObjectsToNull(Object obj) { for (Field field : obj.getClass().getDeclaredFields()) { field.setAccessible(true); try { Object fieldObj = field.get(obj); if (fieldObj != null) { Class fieldType = field.getType(); if (fieldType.isAssignableFrom(String.class)) { if(fieldObj.equals("")) { field.set(obj, null); } } else if (fieldType.isAssignableFrom(Set.class)) { for (Object item : (Set) fieldObj) { setEmtpyStringsAndObjectsToNull(item); } boolean setFielToNull = true; for (Object item : (Set) field.get(obj)) { if(item != null) { setFielToNull = false; break; } } if(setFielToNull) { setFieldToNull(obj, field); } } else if (!isPrimitiveOrWrapper(fieldType)) { setEmtpyStringsAndObjectsToNull(fieldObj); boolean setFielToNull = true; for (Field f : fieldObj.getClass().getDeclaredFields()) { f.setAccessible(true); if(f.get(fieldObj) != null) { setFielToNull = false; break; } } if(setFielToNull) { setFieldToNull(obj, field); } } } } catch (IllegalAccessException e) { System.err.println("Error while setting empty string or object to null:" + e.getMessage()); } } } private void setFieldToNull(Object obj, Field field) throws IllegalAccessException { if(!Modifier.isFinal(field.getModifiers())) { field.set(obj, null); } } private boolean isPrimitiveOrWrapper(Class fieldType) { return fieldType.isPrimitive() || fieldType.isAssignableFrom(Integer.class) || fieldType.isAssignableFrom(Boolean.class) || fieldType.isAssignableFrom(Byte.class) || fieldType.isAssignableFrom(Character.class) || fieldType.isAssignableFrom(Float.class) || fieldType.isAssignableFrom(Long.class) || fieldType.isAssignableFrom(Double.class) || fieldType.isAssignableFrom(Short.class); } |
。