2011-11-10 90 views
5

我正在用Newtonsoft庫解析JSON字符串到相應的.NET對象。解析數組的JSON屬性時遇到問題。有時JSON屬性是一個數組,有時候,它是一個單一的元素。將JSON字符串解析爲.NET對象

實施例:

這是.NET對象:

public class xx 
    { 
     public string yy { get; set; }  
     public List<string> mm{ get; set; }   
    } 

當我收到此JSON:

{ "xx": {"yy":"nn", "mm": [ "zzz", "aaa" ] } } 

我完全可以這樣做:

JsonConvert.DeserializeObject<xx>(json); 

但有時我收到這個J SON:

{ "xx": {"yy":"nn", "mm":"zzz"} } 

由於C#對象上的list屬性,反序列化失敗。

如何定義反序列化同一對象中的兩個JSON字符串的對象(使用List<string>)。

-------- UPDATE -----

的全部WS生成一個XML做一些操作首先..的XML就像

<xx yy='nn'><mm>zzz</mm></xx> 

,如果有更多的內容是:

<xx yy='nn'><mm>zzz</mm><mm>aaa</mm></xx> 

最後WS轉換XML這樣做:

XmlDocument doc = new XmlDocument(); 
doc.LoadXml(xml);   
var json = JsonConvert.SerializeXmlNode(doc); 

併發送給我的JSON ..並在這裏開始我的問題..

+2

不能更改JSON生產方式?因爲這種方式很不一致,而且總是生成一個數組更有意義。 – svick

+1

爲什麼傳入的JSON對象的格式不一致?如果'mm'可能包含多個元素,它應該總是以數組形式傳遞('[]'),而不是簡單的'name:value'對。 –

+0

您應該能夠在將JSON發送到服務器之前對其進行按摩,以使其始終處於可用的格式。顯示用於構造JSON對象的JS。 – arb

回答

1

什麼發送服務發送應符合合同。如果沒有,那麼,或者你毆打發送的開發者,並讓他們修復它,或者發送給你的各種東西都是的合同。可惜你沒有任何元數據可以肯定地知道,你只需要嘗試各種合約,直到有效。

object someValue; 
try 
{ 
    someValue =JsonConvert.DeserializeObject<TypeWithList>(json); 
} 
catch 
{ 
    try 
    { 
     someValue = JsonConvert.DeserializeObject<TypeWithString>(json); 
    } 
    catch 
    { 
    //Darn, yet another type 
    } 
} 
+0

哎。如果事實證明是這樣的話,這將是一場噩夢。我沒有想到他可能無法控制他收到的東西。 – arb

+0

我想在這個解決方案..但我不喜歡。我想也許有一些屬性可以解決.. –

-2

我想你需要看看你的Javascript對象。如果您明確聲明瞭要序列化爲JSON的對象的屬性類型,則不應該遇到任何不一致。

var stringProperty = new String(); 
var arrayProperty = new Array(); 

// Assign value to stringProperty and push elements into arrayProperty 

var object = { 
    stringProperty: stringProperty, 
    arrayProperty: arrayProperty 
}; 

var jsonObject = JSON.stringify(object); 

document.write(jsonObject); 

如果你看一下輸出,你會看到arrayProperty總是序列化到一個數組中是否存在零,一個或多個元素。

5

更新答:

看着JSON.Net地圖XML如何,它採用的方法是什麼看到的是什麼序列化,但如果看到的倍數,它將使一個數組。對於許多具有一致佈局的XML DOM樹來說,這非常棒,但不幸的是,它無法滿足您的需求。

您可以通過在以下文件源中查看函數SerializeGroupedNodes()SerializeNode()的函數來驗證此情況。

XmlNodeConverter.cs source code @ CodePlex, ChangeSet #63616

還有,我以前還以爲可能是矯枉過正,但現在將是有益的,我們知道,從上月底序列化的默認行爲會發生什麼其他的選擇。

Json.Net支持使用從JsonConverter派生的自定義轉換器來根據具體情況映射特定值的情況。

我們可以在序列化一側或反序列化一側處理這個問題。我選擇在反序列化方面編寫解決方案,因爲您可能有其他現有的將XML映射到JSON的原因。

一個很大的好處是你的類可以保持完整除了覆蓋,這就要求你應用一個屬性。以下是演示如何使用JsonAttribute和自定義轉換器類(MMArrayConverter)來解決您的問題的代碼示例。請注意,您可能會想要更徹底地測試此更新,也許可以更新轉換器以處理其他情況,例如,如果您最終遷移到IList<string>或其他一些時髦的案例,如Lazy<List<string>>,甚至可以使其與泛型一起工作。

using Newtonsoft.Json; 
using Newtonsoft.Json.Linq; 
using Newtonsoft.Json.Converters; 

namespace JsonArrayImplictConvertTest 
{ 
    public class MMArrayConverter : JsonConverter 
    { 
     public override bool CanConvert(Type objectType) 
     { 
      return objectType.Equals(typeof(List<string>)); 
     } 

     public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
     { 
      if (reader.TokenType == JsonToken.StartArray) 
      { 
       List<string> parseList = new List<string>(); 
       do 
       { 
        if (reader.Read()) 
        { 
         if (reader.TokenType == JsonToken.String) 
         { 
          parseList.Add((string)reader.Value); 
         } 
         else 
         { 
          if (reader.TokenType == JsonToken.Null) 
          { 
           parseList.Add(null); 
          } 
          else 
          { 
           if (reader.TokenType != JsonToken.EndArray) 
           { 
            throw new ArgumentException(string.Format("Expected String/Null, Found JSON Token Type {0} instead", reader.TokenType.ToString())); 
           } 
          } 
         } 
        } 
        else 
        { 
         throw new InvalidOperationException("Broken JSON Input Detected"); 
        } 
       } 
       while (reader.TokenType != JsonToken.EndArray); 

       return parseList; 
      } 

      if (reader.TokenType == JsonToken.Null) 
      { 
       // TODO: You need to decide here if we want to return an empty list, or null. 
       return null; 
      } 

      if (reader.TokenType == JsonToken.String) 
      { 
       List<string> singleList = new List<string>(); 
       singleList.Add((string)reader.Value); 
       return singleList; 
      } 

      throw new InvalidOperationException("Unhandled case for MMArrayConverter. Check to see if this converter has been applied to the wrong serialization type."); 
     } 

     public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
     { 
      // Not implemented for brevity, but you could add this if needed. 
      throw new NotImplementedException(); 
     } 
    } 

    public class ModifiedXX 
    { 
     public string yy { get; set; } 

     [JsonConverter(typeof(MMArrayConverter))] 
     public List<string> mm { get; set; } 

     public void Display() 
     { 
      Console.WriteLine("yy is {0}", this.yy); 
      if (null == mm) 
      { 
       Console.WriteLine("mm is null"); 
      } 
      else 
      { 
       Console.WriteLine("mm contains these items:"); 
       mm.ForEach((item) => { Console.WriteLine(" {0}", item); }); 
      } 
     } 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      string jsonTest1 = "{\"yy\":\"nn\", \"mm\": [ \"zzz\", \"aaa\" ] }"; 
      ModifiedXX obj1 = JsonConvert.DeserializeObject<ModifiedXX>(jsonTest1); 
      obj1.Display(); 

      string jsonTest2 = "{\"yy\":\"nn\", \"mm\": \"zzz\" }"; 
      ModifiedXX obj2 = JsonConvert.DeserializeObject<ModifiedXX>(jsonTest2); 
      obj2.Display(); 

      // This test is now required in case we messed up the parser state in our converter. 
      string jsonTest3 = "[{\"yy\":\"nn\", \"mm\": [ \"zzz\", \"aaa\" ] },{\"yy\":\"nn\", \"mm\": \"zzz\" }]"; 
      List<ModifiedXX> obj3 = JsonConvert.DeserializeObject<List<ModifiedXX>>(jsonTest3); 
      obj3.ForEach((obj) => { obj.Display(); }); 

      Console.ReadKey(); 
     } 
    } 
} 

原來的答案:

這將是最好的解決你的源接收的JSON,因爲許多人已經指出。您可能希望發佈更新,顯示更新後的評論中的XML如何映射到JSON,因爲這將是整個最佳路線。

但是,如果您發現這是不可能的,並且您希望以某種方式序列化並事後處理變體值,則可以聲明mmobject,然後處理可能的事件您可以使用JSON.Net的Linq支持。在您描述的兩種情況下,您會發現聲明mmobject類型將導致null,stringJArray通過調用DeserializeObject<>而被指定爲mm

下面是一個代碼示例,顯示了這一行爲。在其他情況下,您也可以收到一個JObject,這也在本示例中介紹。請注意,成員函數mmAsList()會爲您修補差異。還請注意,我已通過返回null代替List<string>處理了null;你可能會想要修改這個實現。

using Newtonsoft.Json; 
using Newtonsoft.Json.Linq; 

namespace JsonArrayUnionTest 
{ 
    public class ModifiedXX 
    { 
     public string yy { get; set; } 
     public object mm { get; set; } 

     public List<string> mmAsList() 
     { 
      if (null == mm) { return null; } 
      if (mm is JArray) 
      { 
       JArray mmArray = (JArray)mm; 
       return mmArray.Values<string>().ToList(); 
      } 

      if (mm is JObject) 
      { 
       JObject mmObj = (JObject)mm; 
       if (mmObj.Type == JTokenType.String) 
       { 
        return MakeList(mmObj.Value<string>()); 
       } 
      } 

      if (mm is string) 
      { 
       return MakeList((string)mm); 
      } 

      throw new ArgumentOutOfRangeException("unhandled case for serialized value for mm (cannot be converted to List<string>)"); 
     } 

     protected List<string> MakeList(string src) 
     { 
      List<string> newList = new List<string>(); 
      newList.Add(src); 
      return newList; 
     } 

     public void Display() 
     { 
      Console.WriteLine("yy is {0}", this.yy); 
      List<string> mmItems = mmAsList(); 
      if (null == mmItems) 
      { 
       Console.WriteLine("mm is null"); 
      } 
      else 
      { 
       Console.WriteLine("mm contains these items:"); 
       mmItems.ForEach((item) => { Console.WriteLine(" {0}", item); }); 
      } 
     } 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      string jsonTest1 = "{\"yy\":\"nn\", \"mm\": [ \"zzz\", \"aaa\" ] }"; 
      ModifiedXX obj1 = JsonConvert.DeserializeObject<ModifiedXX>(jsonTest1); 
      obj1.Display(); 

      string jsonTest2 = "{\"yy\":\"nn\", \"mm\": \"zzz\" }"; 
      ModifiedXX obj2 = JsonConvert.DeserializeObject<ModifiedXX>(jsonTest2); 
      obj2.Display(); 

      Console.ReadKey(); 
     } 
    } 
} 
+1

有趣的解決方案..謝謝你meklarian! –

0

在你的情況,你可以直接使用靜態方法從JsonConvert類

PopulateObject(字符串值,對象目標,JsonSerializerSettings設置);

通過JsonSerializerSettings對象作爲

new JsonSerializerSettings(){TypeNameHandling = TypeNameHandling.All})