Gson:如何在不使用注释的情况下排除特定字段的序列化

454

我正在尝试学习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来排除字段名称,例如firstNamecountry,但是似乎无法排除某些字段的属性,例如country.name
使用方法public boolean shouldSkipField(FieldAttributes fa),FieldAttributes不包含足够的信息来将该字段与过滤器(例如country.name)匹配。
附言:我想避免使用注释,因为我想改进并使用RegEx来过滤字段。
编辑:我正在尝试查看是否可能模拟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。我也不想排除Country作为类型。 因此,类型是相同的,实际上是我想要确定并排除对象图中的位置。


1
直到版本2.2,我仍然无法指定要排除的字段路径。http://flexjson.sourceforge.net/似乎是一个不错的替代选择。 - Liviu T.
请看一下我在堆栈溢出上对一个类似问题的答案。它基于创建一个自定义的 JsonSerializer 对某种类型(比如你的情况中的Country)进行序列化时应用一个 ExclusionStrategy,以决定哪些字段需要序列化。 - pirho
https://dev59.com/OXA65IYBdhLWcg3w9DmF - Pangamma
17个回答

9
或者说哪些字段不会被公开:
Gson gson = gsonBuilder.excludeFieldsWithModifiers(Modifier.TRANSIENT).create();

关于你的 class 属性:

private **transient** boolean nameAttribute;

19
默认情况下,瞬时和静态字段被排除在外;因此不需要调用excludeFieldsWithModifiers() - Derek Shockey

7

另一种方法(特别适用于需要在运行时决定排除字段的情况)是向您的gson实例注册TypeAdapter。以下是示例:

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

2
在Kotlin中,可以使用@Transient来忽略字段...例如。
data class MyClass{
@Transient var  myVar: Boolean
//....
}

1

我只需添加@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 {

适配器类中:
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);
}

1
这是我经常使用的方法:
Gson的默认行为是忽略空对象字段。
这意味着,如果Java对象中的字段为空,Gson对象不会将其序列化为JSON。 Gson会排除这些字段。
您可以使用此函数将某个对象转换为null或按自己的设置进行设置。
     /**
   * 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);
  }

1
我有 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)

0
使用不同的DTO来缓存对象。
例如,您可以创建一个名为UserCached的类,并仅保留您需要的字段。然后,创建映射器以进行对象的双向映射。 Mapstruct非常适合这个任务。
这种方法解决了问题,解耦了应用程序,并使对主DTO的更改更加安全。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接