2013-07-29 16 views
1

我正在開發一個使用JSON.NET與第三方REST API接口的ASP.NET應用程序。該API按以下格式返回JSON:有沒有一種方法來反序列化我的JSON而不使用每個項目的包裝類?

[ 
    { 
     user: { 
      id: 12345, 
      first_name: "John", 
      last_name: "Smith", 
      created_at: "11/12/13Z00:00:00" 
     } 
    }, 
    { 
     user: { 
      id: 12346, 
      first_name: "Bob", 
      last_name: "Adams", 
      created_at: "12/12/13Z00:00:00" 
     } 
    } 
] 

user場是多餘的,但我沒有在API控制,所以我不得不忍受它。我目前使用下面的代碼來反序列化JSON:

// Deserialize 
var responseBody = JsonConvert.DeserializeObject<IEnumerable<UserWrapper>>(responseString); 

// Access Properties 
foreach (var userWrapper in responseBody) 
{ 
    var firstName = userWrapper.User.FirstName 
} 

// Model classes 
public class UserWrapper 
{ 
    [JsonProperty(PropertyName = "user")] 
    public User User { get; set; } 
} 

public class User 
{ 
    [JsonProperty(PropertyName = "id")] 
    public string ID { get; set; } 

    [JsonProperty(PropertyName = "first_name")] 
    public string FirstName { get; set; } 

    [JsonProperty(PropertyName = "last_name")] 
    public string LastName { get; set; } 

    [JsonProperty(PropertyName = "created_at")] 
    public DateTime CreatedAt { get; set; } 
} 

擁有包裝類有點難看。所以我的問題是:

有沒有辦法使用JSON.NET消除包裝類?

回答

1

是的,您可以通過爲User類創建自定義轉換器並在反序列化過程中使用該轉換器來完成此操作。該轉換器的代碼可能是這個樣子:

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

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     JObject wrapper = JObject.Load(reader); 
     return wrapper["user"].ToObject<User>(); 
    } 

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     writer.WriteStartObject(); 
     writer.WritePropertyName("user"); 
     writer.WriteRawValue(JsonConvert.SerializeObject(value)); 
     writer.WriteEndObject(); 
    } 
} 

保持完全按照你定義它的User類(所有JsonProperty裝飾品)。 然後,您可以反序列化JSON的和訪問屬性是這樣的:

var users = JsonConvert.DeserializeObject<List<User>>(responseString, new UserConverter()); 

foreach (var user in users) 
{ 
    var firstName = user.FirstName 
    // etc... 
} 

UserWrapper類不再需要,可以將其刪除。

編輯

重要提示:上述解決方案將工作,如果你裝飾你的User類的[JsonConverter]屬性。這是因爲自定義轉換器寫入後,會使用串行器的第二個副本來處理User反序列化,一旦我們降低級別以獲取真實用戶數據。如果你裝飾了這個類,那麼序列化器的所有拷貝都會嘗試使用這個轉換器,最終你會得到一個自引用循環。

如果您寧願使用[JsonConverter]屬性,則自定義轉換器類將不得不手動對User類的所有屬性進行反序列化。採用這種方法,代碼應該是這樣的,而不是:

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
{ 
    JObject wrapper = JObject.Load(reader); 
    JToken user = wrapper["user"]; 
    return new User 
    { 
     ID = user["id"].Value<string>(), 
     FirstName = user["first_name"].Value<string>(), 
     LastName = user["last_name"].Value<string>(), 
     CreatedAt = user["created_at"].Value<DateTime>() 
    }; 
} 

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
{ 
    User user = (User)value; 
    writer.WriteStartObject(); 
    writer.WritePropertyName("user"); 
    writer.WriteStartObject(); 
    writer.WritePropertyName("id"); 
    writer.WriteValue(user.ID); 
    writer.WritePropertyName("first_name"); 
    writer.WriteValue(user.FirstName); 
    writer.WritePropertyName("last_name"); 
    writer.WriteValue(user.LastName); 
    writer.WritePropertyName("created_at"); 
    writer.WriteValue(user.CreatedAt); 
    writer.WriteEndObject(); 
    writer.WriteEndObject(); 
} 

這種方法的明顯的問題是,你犧牲可維護性的一點點:如果你添加更多的屬性你User類,你要記住改變你的UserConverter以匹配。

+0

如何實現WriteJson方法?我曾嘗試使用編寫器編寫用戶屬性,然後使用序列化程序對該值進行序列化,但是這會導致自引用循環,因爲序列化程序只是在另一個UserConverter上使用調用WriteJson。請參閱代碼: –

+0

http://pastebin.com/B2QMVtyM –

+0

您近在咫尺 - 只需使用新的序列化器,而不是傳入的新序列器。新的序列器將不會提供轉換器的引用,因此您將不會獲得通告參考問題。我已經用代碼更新了我的答案。唯一的缺點是它不會選擇你可能使用的任何其他序列化器設置 - 如果你需要這些設置,你必須手動將它們從外部串行器複製到內部。 –

相關問題