【问题标题】:Gson: parameter get serialised even though it has @Expose(serialize = false)Gson:参数得到序列化,即使它有@Expose(serialize = false)
【发布时间】:2017-03-27 13:29:24
【问题描述】:

我正在为 JSON API 编写 SDK,但遇到了一个看似奇怪的问题。 API 在 POST 数据验证方面非常严格,并且在更新资源时不允许出现某些参数,例如 id。为此,我在资源类的 ID 字段中添加了@Expose(serialize = false)。然而,它似乎仍然序列化这个字段,导致请求被拒绝。资源类大致如下:

public class Organisation extends BaseObject
{
    public static final Gson PRETTY_PRINT_JSON = new GsonBuilder()
            .setPrettyPrinting()
            .create();

    @Expose(serialize = false)
    @SerializedName("_id")
    private String id;

    @SerializedName("email")
    private String email;

    @SerializedName("name")
    private String name;

    @SerializedName("parent_id")
    private String parentId;

    public String toJson()
    {
        return PRETTY_PRINT_JSON.toJson(this);
    }
}

我的单元测试通过 API 创建了一个 Organisation 的实例,将新创建的实例作为类参数保存到测试类中,并调用一个更新方法,该方法将通过更新新资源来测试 SDK 的更新实现。这就是它出错的地方。即使在新的 Organisation 上调用 toJson() 方法以将其序列化为 JSON 以用于更新请求,_id 字段仍然存在,导致 API 拒绝更新。测试代码如下。注意代码中的 cmets。

@Test
public void testCreateUpdateAndDeleteOrganisation() throws RequestException
{
    Organisation organisation = new Organisation();
    organisation.setParentId(this.ORGANISATION_ID);
    organisation.setName("Java Test Organisation");

    Organisation newOrganisation = this.MySDK.organisation.create(organisation);
    this.testOrganisation(newOrganisation);
    this.newOrganisation = newOrganisation;

    this.testUpdateOrganisation();
}

public void testUpdateOrganisation() throws RequestException
{
    // I tried setting ID to null, but that doesn't work either
    // even though I've set Gson to not serialise null values
    this.newOrganisation.setId(null);
    this.newOrganisation.setName(this.newName);

    // For debugging
    System.out.println(this.newOrganisation.toJson());

    Organisation updatedOrganisation = this.MySDK.organisation.update(this.newOrganisation.getId(), this.newOrganisation);

    this.testOrganisation(updatedOrganisation);
    assertEquals(newOrganisation.getName(), this.newName);

    this.testDeleteOrganisation();
}

谁能发现我做错了什么?我觉得这与实例已经具有/具有 ID 值的事实有关,但如果我明确告诉它不要序列化它,这应该没关系吧?

提前感谢您的帮助。

编辑:在this.MySDK.organisation.update(this.newOrganisation.getId(), this.newOrganisation);,不编辑组织实例。给定 ID 仅添加到 SDK 将发布到的 URL (POST /organisation/{id})

【问题讨论】:

  • 尝试改为transient
  • @cricket_007 我之前做过,但在反序列化时它被忽略了。我使用@Expose 对其进行了更多控制
  • 看到这个了吗? futurestud.io/tutorials/…
  • @cricket_007 是的,我知道。你建议我从那个教程中改变什么?引用:“使用 \@Expose 的替代方法是将字段声明为瞬态。瞬态字段也不会(反)序列化。但是,您没有像使用 \@Expose 那样完全控制。您不能停用一个方向,transient 将始终完全关闭该属性的转换。”

标签: java json gson


【解决方案1】:

感谢@peitek 指出@Expose 会被忽略,除非将.excludeFieldsWithoutExposeAnnotation() 添加到GsonBuilder()。但是,我选择不采用这条路线,因为它需要我将@Expose 添加到我的模型类的每个参数中,只是为了忽略序列化中的一个字段。相反,我编写了一个 ExclusionStrategy 来检查参数上是否存在自定义 SkipSerialisation 注释。我按如下方式实现了这些:

完整的GsonBuilder 策略:

public static final Gson PRETTY_PRINT_JSON = new GsonBuilder()
        .addSerializationExclusionStrategy(new ExclusionStrategy()
        {
            @Override
            public boolean shouldSkipField(FieldAttributes f)
            {
                return f.getAnnotation(SkipSerialisation.class) != null;
            }

            @Override
            public boolean shouldSkipClass(Class<?> clazz)
            {
                return false;
            }
        })
        .setPrettyPrinting()
        .create();

还有注解:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface SkipSerialisation
{
}

现在我可以做到了

@SkipSerialisation
@SerializedName("_id")
private String id;

它有效!

【讨论】:

  • f.getAnnotation(SkipSerialisation.class) != null 可以替换为f.isAnnotationPresent(SkipSerialisation.class)
  • @LyubomyrShaydariv 更好!我会用那个
  • @LyubomyrShaydariv 那是哪个版本的 Gson?我没有那个方法。
  • 对不起对不起对不起!我对 docs.oracle.com/javase/7/docs/api/java/lang/reflect/… 感到困惑。真丢人!
  • @LyubomyrShaydariv 啊!不是问题。否则你就是 Java 天才。
【解决方案2】:

正如您在 cmets 中提到的,@Expose 应该是比transient 更好的选择。需要注意的是,默认的 Gson 实例不考虑 @Expose 注释!无论您设置什么选项,它都会忽略它。

如果要激活@Expose选项,需要自定义Gson。根据您上面的代码,将其更改为:

public static final Gson PRETTY_PRINT_JSON = new GsonBuilder()
        .setPrettyPrinting()
        .excludeFieldsWithoutExposeAnnotation();
        .create();

您的@Expose(serialize = false) 应该在序列化期间处于活动状态并被排除在外。

【讨论】:

  • 您好,谢谢您的回答。我认为excludeFieldsWithoutExposeAnnotation() 完全按照名称所暗示的那样做,但如果我想做一些具体的事情,Expose 仍然可以工作。这确实意味着我必须将 Expose 添加到所有模型中的每个字段,对吗?
  • 是的,同意。该函数名称并不理想。是的,您需要在相关模型的每个字段中至少添加一个 @Expose()
  • 谢谢。查看文档后,它清楚地表明:如果我们使用 new Gson() 创建 Gson,并执行 toJson() 和 fromJson() 方法,那么@Expose 不会对序列化和反序列化产生任何影响。 (...) howtodoinjava.com/gson/gson-exclude-or-ignore-fields
【解决方案3】:

我只是想在这里补充一下,您可以像这样以所需的方式使用 Expose:

builder.addSerializationExclusionStrategy(new ExclusionStrategy() {
        @Override
        public boolean shouldSkipField(FieldAttributes f) {
            Expose annotation = f.getAnnotation(Expose.class);
            if(annotation != null)
                return !annotation.serialize();
            else
                return false;
        }

        @Override
        public boolean shouldSkipClass(Class<?> clazz) {
            Expose annotation = clazz.getAnnotation(Expose.class);
            if(annotation != null)
                return !annotation.serialize();
            else
                return false;
        }
    });
    builder.addDeserializationExclusionStrategy(new ExclusionStrategy() {
        @Override
        public boolean shouldSkipField(FieldAttributes f) {
            Expose annotation = f.getAnnotation(Expose.class);
            if(annotation != null)
                return !annotation.deserialize();
            else
                return false;
        }

        @Override
        public boolean shouldSkipClass(Class<?> clazz) {
            Expose annotation = clazz.getAnnotation(Expose.class);
            if(annotation != null)
                return !annotation.deserialize();
            else
                return false;
        }
    });

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-04-17
    • 2018-06-20
    • 2018-12-23
    • 2018-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多