我正在为 JSON API 编写 SDK,但遇到了一个看似奇怪的问题。该 API 在 POST 数据验证方面非常严格,并且不允许在更新资源时出现某些参数,例如
id
。为此,我添加了 @Expose(serialize = false)
我的资源类的 ID 字段。然而,它似乎仍然序列化该字段,导致请求被拒绝。资源类大致如下:
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 的更新实现。这就是出错的地方。即使在新的 toJson()
上调用 Organisation
方法将其序列化为 JSON 以用于更新请求,_id
字段仍然存在,导致 API 拒绝更新。测试代码如下。注意代码中的注释。
@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 将 POST 到的 URL (POST /organisation/{id}
)
感谢@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;
并且有效!
正如您在评论中提到的,
@Expose
应该是比transient
更好的选择。需要注意的是,默认 Gson 实例不考虑 @Expose
注释!无论您设置什么选项,它都会忽略它。
如果要激活
@Expose
选项,则需要自定义Gson。根据上面的代码,将其更改为:
public static final Gson PRETTY_PRINT_JSON = new GsonBuilder()
.setPrettyPrinting()
.excludeFieldsWithoutExposeAnnotation();
.create();
您的
@Expose(serialize = false)
应处于活动状态并在序列化期间被排除。
我只是想在这里补充一点,你可以像这样以所需的方式使用 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;
}
});
GsonBuilder()
/** WARN : the function result is :
* just Serialization the field and class which with @Expose
* */
/*.excludeFieldsWithoutExposeAnnotation()*/
/** addXxxxxxxxxExclusionStrategy Function is the best selection
define yourself exclusion rule
*/
.addSerializationExclusionStrategy(
object : ExclusionStrategy {
override fun shouldSkipField(fieldAttr: FieldAttributes?): Boolean {
return fieldAttr?.getAnnotation(Expose::class.java)?.serialize == true
}
override fun shouldSkipClass(clazz: Class<*>?): Boolean {
return clazz?.getAnnotation(Expose::class.java)?.serialize == true
}
})
.addDeserializationExclusionStrategy(object : ExclusionStrategy {
override fun shouldSkipField(fieldAttr: FieldAttributes?): Boolean {
return fieldAttr?.getAnnotation(Expose::class.java)?.deserialize == true
}
override fun shouldSkipClass(clazz: Class<*>?): Boolean {
return clazz?.getAnnotation(Expose::class.java)?.deserialize == true
}
})
.create()