2017-03-27 54 views
2

我正在爲JSON API編寫一個SDK,我遇到了一個看似奇怪的問題。該API在POST數據驗證方面相當嚴格,並且在更新資源時不允許某些參數存在,如id。爲此,我添加了@Expose(serialize = false)我的資源類的ID字段。但它似乎仍然序列化該字段,導致請求被拒絕。資源類大致如下:Gson:即使它具有@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拒絕更新。測試代碼如下。注意代碼中的註釋。

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

+0

試着讓它變成'transient'而不是 –

+0

@ cricket_007我之前做過,但是在反序列化時忽略了它。我用'@ Expose'有更多的控制權 –

+1

看過? https://futurestud.io/tutorials/gson-model-annotations-how-to-ignore-fields-with-expose –

回答

2

感謝@peitek指出@Expose被忽略,除非.excludeFieldsWithoutExposeAnnotation()被添加到GsonBuilder()。但是,我選擇不採用這種方式,因爲它需要我將@Expose添加到我的模型類的每個參數中,以便忽略序列化中的一個字段。相反,我編寫了一個ExclusionStrategy,用於檢查參數上是否存在定製的註釋。我實現這些如下:

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; 

和它的工作原理!

+0

'f.getAnnotation(SkipSerialisation.class)!= null'可以替換爲'f.isAnnotationPresent(SkipSerialisation.class)'。 –

+0

@LyubomyrShaydariv更棒!我會用那 –

+0

@LyubomyrShaydariv在哪個版本的Gson是那個?我沒有那種方法。 –

4

正如你在你的評論中提到的,@Expose應該是在這裏比transient更好的選擇。請注意,默認Gson實例不考慮@Expose註釋!無論您設置什麼選項,它都會忽略它。

如果你想激活@Expose選項,你需要定製Gson。基於上面的代碼,將其更改爲:

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

@Expose(serialize = false)應該是積極和序列化過程中排除。

+0

嗨,謝謝你的回答。我認爲'excludeFieldsWithoutExposeAnnotation()'完全按照名稱的建議進行,但如果我想要做某些特定的事情,「Expose」仍然可以工作。這是否意味着我必須將Expose添加到所有模型的每個字段中? –

+0

是的,同意了。該函數名稱不理想。是的,您需要在相關模型的每個字段中至少添加一個@Expose()。 – peitek