2014-03-28 109 views
13

我使用JSON.NET對我的對象進行序列化以連接到REST API。我的對象中需要序列化爲JSON的其中一個屬性具有動態屬性名稱。 如果此屬性的結構中包含的值是數值,那麼JSON屬性是「type_id」,但是如果此值是字符串值,那麼JSON屬性名稱是「type_code」。我試圖使用自定義JsonConverter這一點,但我得到這個消息的JsonWriterException當我試圖序列:序列化使用JSON.NET的對象的動態屬性名稱

「令牌屬性名國家財產會導致一個無效的JSON對象路徑'」

下面是我的對象的一個​​子集,如下面看到我沒有爲我的對象指定一個屬性名作爲這樣的:

[JsonProperty("title",Required=Required.Always,Order=1)] 
public string Title { get; set; } 

[JsonProperty("date",Order=3)] 
[JsonConverter(typeof(IsoDateTimeConverter))] 
public DateTime Date { get; set; } 

[JsonProperty(Order=2)] 
[JsonConverter(typeof(TypeIdentifierJsonConverter))] 
public TypeIdentifier DocTypeIdentifier { get; set; } 

在TypeIdentifier類中,我有以下我WriteJson()方法:

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
{ 
    TypeIdentifier docTypeId; 
    id= (TypeIdentifier) value; 

    writer.WritePropertyName(id.ParameterName); 
    writer.WriteValue(id.Value);   
} 

然而,我假設它默認爲對象的屬性,而不是我的自定義一個名稱,使兩個屬性名稱爲JSON字符串中的單個值。如何爲此動態設置屬性名稱,因爲JsonPropertyAttribute標記在未明確指定時似乎拉取對象的屬性名稱?

注意:此對象將永遠不需要從此應用程序反序列化。

編輯:這個目的是標記有[JsonObject(MemberSerialization.OptIn)]屬性

+0

你有對象上的[Serializable]屬性? – Robert

+0

@Robert使用JSON.NET不是內置的.NET序列化,意思是那些被忽略。不過,我確實有爲該類設置的[JsonObject(MemberSerialization.OptIn)]屬性。 – JNYRanger

回答

21

JsonConverter不能設置在父對象中的屬性的名稱。當調用轉換器的WriteJson方法時,屬性名稱已被寫入JSON;作者只希望有一點值得指出。這就是你遇到錯誤的原因。爲了使這個工作,自定義轉換器將不得不爲父對象。該轉換器將負責編寫其子項的屬性名稱和值。

後續

這是可以寫入,使得施加給它的JSON屬性仍然尊重對父對象的轉換器,同時還實現你想要的結果。我將概述下面的方法。

首先,稍微設置一下。既然你沒有說你的班級被稱爲什麼,我將假設這個例子叫做Document。我們只需要對其進行實質性更改,即從DocTypeIdentifier屬性中刪除[JsonConverter]屬性。因此,我們有:

[JsonObject(MemberSerialization.OptIn)] 
class Document 
{ 
    [JsonProperty("title", Required = Required.Always, Order = 1)] 
    public string Title { get; set; } 

    [JsonProperty("date", Order = 3)] 
    [JsonConverter(typeof(IsoDateTimeConverter))] 
    public DateTime Date { get; set; } 

    [JsonProperty(Order = 2)] 
    public TypeIdentifier DocTypeIdentifier { get; set; } 

    public string OtherStuff { get; set; } 
} 

您還沒有表現出對TypeIdentifier類的代碼,所以我就假設它看起來像這樣,例如緣故

class TypeIdentifier 
{ 
    public string Value { get; set; } 
    public string ParameterName { get; set; } 
} 

有了這樣的方式,我們可以製造轉換器。這種方法非常簡單:我們將Document加載到JObject中,利用它尊重所應用的屬性的優點,然後返回並修復DocTypeIdentifier的序列化,因爲它需要特殊處理。一旦我們有了,我們將JObject寫入JsonWriter。下面是代碼:

class DocumentConverter : JsonConverter 
{ 
    public override bool CanConvert(Type objectType) 
    { 
     return (objectType == typeof(Document)); 
    } 

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     Document doc = (Document)value; 

     // Create a JObject from the document, respecting existing JSON attribs 
     JObject jo = JObject.FromObject(value); 

     // At this point the DocTypeIdentifier is not serialized correctly. 
     // Fix it by replacing the property with the correct name and value. 
     JProperty prop = jo.Children<JProperty>() 
          .Where(p => p.Name == "DocTypeIdentifier") 
          .First(); 

     prop.AddAfterSelf(new JProperty(doc.DocTypeIdentifier.ParameterName, 
             doc.DocTypeIdentifier.Value)); 
     prop.Remove(); 

     // Write out the JSON 
     jo.WriteTo(writer); 
    } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     throw new NotImplementedException(); 
    } 
} 

現在我們有轉換器,但美中不足的是,我們不能簡單地用[JsonConverter]屬性裝飾Document類才能使用它。如果我們這樣做了,那麼當我們將文檔加載到JObject時,轉換器會嘗試使用它自己,最終會產生遞歸循環。相反,我們需要創建一個轉換器的實例並通過設置將它傳遞給序列化器。轉換器的CanConvert方法確保它在正確的類上使用。 JObject.FromObject方法在內部使用不同的串行器實例,因此它看不到DocumentConverter,因此不會出現問題。

JsonSerializerSettings settings = new JsonSerializerSettings(); 
settings.Converters.Add(new DocumentConverter()); 

string json = JsonConvert.SerializeObject(doc, settings); 

這裏是在運行中的轉換器演示:

class Program 
{ 
    static void Main(string[] args) 
    { 
     Document doc = new Document 
     { 
      Title = "How to write a JSON converter", 
      Date = DateTime.Today, 
      DocTypeIdentifier = new TypeIdentifier 
      { 
       ParameterName = "type_id", 
       Value = "26" 
      }, 
      OtherStuff = "this should not appear in the JSON" 
     }; 

     JsonSerializerSettings settings = new JsonSerializerSettings(); 
     settings.Converters.Add(new DocumentConverter()); 
     settings.Formatting = Formatting.Indented; 

     string json = JsonConvert.SerializeObject(doc, settings); 
     Console.WriteLine(json); 
    } 
} 

這裏是從上面的輸出:

{ 
    "title": "How to write a JSON converter", 
    "type_id": "26", 
    "date": "2014-03-28T00:00:00-05:00" 
} 
+0

所以基本上我需要爲整個對象編寫一個轉換器,並且使用用於自動序列化的屬性不會在這裏執行這個技巧?由於我已經有了幾個用於內部對象的轉換器,當我開始使用這些字段時,是否可以從父轉換器調用它們,因此我不需要重寫這些轉換器? – JNYRanger

+1

有一種方法可以編寫轉換器,以使屬性的屬性仍然受到尊重(意味着其他轉換器應該自動調用),但是,您不能在父項中放置一個'[JsonConverter]'屬性對象,否則在轉換器嘗試使用它時會導致遞歸循環。給我幾分鐘,我會試着舉個例子來演示。 –

+0

非常感謝。一個例子會非常有幫助,我一直在對牆進行研究,並對DefaultContractResolver進行了擴展,似乎並不符合我的需求,我也不想在第一個中使用那麼多的反射地點。 – JNYRanger

相關問題