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 и добавить ExclusionStrategy для имени поля, такого как firstName
или country
, но мне кажется, что мне не удается исключить свойства определенных полей, например country.name
.
Используя метод public boolean shouldSkipField(FieldAttributes fa)
, FieldAttributes не содержит достаточной информации, чтобы соответствовать полю с фильтром, например country.name
.
Я был бы признателен за любую помощь в решении этой проблемы.
P.S: Я хочу избежать аннотаций, так как я хочу улучшить это и использовать RegEx для фильтрации полей.
Спасибо
Изменить. Я пытаюсь понять, можно ли эмулировать поведение плагин Struts2 JSON
с помощью Gson
<interceptor-ref name="json">
<param name="enableSMD">true</param>
<param name="excludeProperties">
login.password,
studentList.*\.sin
</param>
</interceptor-ref>
Edit: Я снова открыл вопрос со следующим дополнением:
Я добавил второе поле с тем же типом, чтобы уточнить эту проблему. В основном я хочу исключить country.name
, но не countrOfBirth.name
. Я также не хочу исключать страну как тип.
Таким образом, типы являются тем же самым фактическим местом в графе объектов, который я хочу точно определить и исключить.
Ответы
Ответ 1
Любые поля, которые вы не хотите сериализовать вообще, должны использовать модификатор "переходный", и это также относится к сериализаторам json (по крайней мере, это относится к нескольким, которые я использовал, включая gson).
Если вы не хотите, чтобы имя отображалось в сериализованном json, дайте ему временное ключевое слово, например:
private transient String name;
Подробнее в документации Gson
Ответ 2
Нишант обеспечил хорошее решение, но там был более простой способ. Просто отметьте нужные поля аннотацией @Expose, например:
@Expose private Long id;
Оставьте все поля, которые вы не хотите сериализовать. Затем просто создайте объект Gson таким образом:
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
Ответ 3
Итак, вы хотите исключить 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);
Это возвращает:
{ "middleName": "J.", "initials": "P.F", "lastName": "Fry", "country": { "id": 91}}
Я предполагаю, что объект страны инициализируется с id = 91L
в классе ученика.
Вы можете стать модным. Например, вы не хотите сериализовать любое поле, которое содержит в своем имени строку "name". Сделай это:
public boolean shouldSkipField(FieldAttributes f) {
return f.getName().toLowerCase().contains("name");
}
Это вернет:
{ "initials": "P.F", "country": { "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);
Возвращает:
{ "firstName": "Philip" , "middleName": "J.", "initials": "P.F", "lastName": "Fry", "country": { "id": 91 }}
Ответ 4
Прочитав все доступные ответы, я узнал, что наиболее гибким, в моем случае, было использование пользовательской аннотации @Exclude
. Таким образом, я реализовал простую стратегию для этого (я не хотел отмечать все поля с помощью @Expose
, и я не хотел использовать transient
, который противоречил сериализации приложения Serializable
):
Аннотация:
@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();
Ответ 5
Я столкнулся с этой проблемой, в которой у меня было небольшое количество полей, которые я хотел исключить только из сериализации, поэтому я разработал довольно простое решение, которое использует аннотацию 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();
Теперь я могу легко исключить несколько полей с аннотациями @Expose(serialize = false)
или @Expose(deserialize = false)
(обратите внимание, что значением по умолчанию для обоих атрибутов @Expose
является true
). Разумеется, вы можете использовать @Expose(serialize = false, deserialize = false)
, но это более точно достигается путем объявления поля transient
вместо этого (которое все еще вступает в силу с этими пользовательскими стратегиями исключения).
Ответ 6
Вы можете исследовать дерево json с помощью gson.
Попробуйте что-то вроде этого:
gson.toJsonTree(student).getAsJsonObject()
.get("country").getAsJsonObject().remove("name");
Вы также можете добавить некоторые свойства:
gson.toJsonTree(student).getAsJsonObject().addProperty("isGoodStudent", false);
Протестировано с помощью gson 2.2.4.
Ответ 7
Я разработал класс factory для поддержки этой функции. Передайте любую комбинацию полей или классов, которые вы хотите исключить.
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);
}
Ответ 8
Я решил эту проблему с помощью специальных аннотаций.
Это мой класс аннотации 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;
}
Ответ 9
Или можно сказать, какие поля не будут отображаться с помощью:
Gson gson = gsonBuilder.excludeFieldsWithModifiers(Modifier.TRANSIENT).create();
в вашем классе по атрибуту:
private **transient** boolean nameAttribute;
Ответ 10
Я использовал эту стратегию:
Я исключил каждое поле, которое не, помечено аннотацией @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": "Я увижу это" }
Ответ 11
Другой подход (особенно полезный, если вам нужно принять решение об исключении поля во время выполнения) - зарегистрировать TypeAdapter с вашим экземпляром gson. Пример ниже:
Gson gson = new GsonBuilder()
.registerTypeAdapter(BloodPressurePost.class, new BloodPressurePostSerializer())
В приведенном ниже случае сервер будет ожидать одно из двух значений, но поскольку оба они были 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;
}
}
Ответ 12
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
)
Выход:
{"serialized_field_1":"VALUE1","serialized_field_2":"VALUE2"}
Ответ 13
Я работаю, просто помещая аннотацию @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);
}
Ответ 14
У меня есть версия 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)
Ответ 15
Это то, что я всегда использую:
Поведение по умолчанию, реализованное в Gson, заключается в том, что нулевые поля объекта игнорируются.
Означает, что объект Gson не сериализует поля с нулевыми значениями в JSON. Если поле в объекте Java является нулевым, Gson исключает его.
Вы можете использовать эту функцию, чтобы преобразовать какой-либо объект в ноль или установить его самостоятельно
/**
* 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);
}