2011-11-10 94 views
5

Tôi đang phân tích một chuỗi JSON thành một đối tượng .NET tương ứng với thư viện Newtonsoft. Tôi có một vấn đề phân tích cú pháp thuộc tính JSON là mảng. Đôi khi thuộc tính JSON là một mảng, lần khác, nó là một phần tử duy nhất.Phân tích chuỗi JSON thành .NET Object

Ví dụ:

Đây là đối tượng .NET:

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

Khi tôi nhận được JSON này:

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

tôi hoàn toàn có thể làm:

JsonConvert.DeserializeObject<xx>(json); 

Nhưng đôi khi tôi nhận được J SON:

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

Và quá trình deserialization không thành công do thuộc tính danh sách trên đối tượng C#.

Làm cách nào để xác định một đối tượng để deserialize hai chuỗi JSON trong cùng một đối tượng (với List<string>).

-------- CẬP NHẬT -----

Trước hết một WS tạo ra một XML thực hiện một số hoạt động .. XML cũng giống như

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

và nếu có nhiều yếu tố là:

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

cuối cùng là WS chuyển đổi XML này thực hiện:

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

và gửi cho tôi json .. và tại đây bắt đầu sự cố của tôi ..

+2

bạn không thể thay đổi cách JSON được sản xuất? Bởi vì cách này khá không nhất quán và luôn tạo ra một mảng có ý nghĩa hơn. – svick

+1

Tại sao định dạng của đối tượng JSON đến không nhất quán? Nếu 'mm' có thể chứa nhiều hơn một phần tử, nó luôn luôn được chuyển thành một mảng (' [] ') và không bao giờ là một cặp' name: value' đơn giản. –

+0

Bạn sẽ có thể xoa bóp JSON trước khi gửi nó đến máy chủ để nó luôn ở định dạng hoạt động. Hiển thị JS mà bạn sử dụng để xây dựng đối tượng JSON. – arb

Trả lời

1

Dịch vụ gửi gửi được cho là phù hợp với hợp đồng. Nếu nó không, sau đó tốt, hoặc là bạn đánh bại các nhà phát triển gửi và làm cho họ sửa chữa nó, hoặc những thứ khác nhau được gửi đến cho bạn hợp đồng. Một điều đáng tiếc bạn không có bất kỳ siêu dữ liệu để biết chắc chắn, bạn sẽ chỉ phải thử một loạt các hợp đồng cho đến khi một công trình.

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

Ouch. Đây sẽ là một cơn ác mộng nếu điều này hóa ra là như vậy. Tôi đã không nghĩ rằng anh ta có thể không kiểm soát được những gì anh ta đang nhận được. – arb

+0

Tôi nghĩ trong giải pháp này .. nhưng tôi không thích chút nào. Tôi nghĩ có lẽ với một số loại tài sản tôi có thể giải quyết .. –

-2

Tôi nghĩ bạn cần xem đối tượng Javascript của mình. Nếu bạn khai báo một cách rõ ràng loại thuộc tính của đối tượng mà bạn sắp sắp xếp thành JSON, bạn không nên chạy vào bất kỳ mâu thuẫn nào.

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); 

Nếu bạn nhìn vào đầu ra, bạn sẽ thấy rằng arrayProperty luôn được nối tiếp thành một mảng cho dù không có, một hoặc nhiều phần tử.

5

Cập nhật trả lời:

Nhìn vào cách bản đồ JSON.Net XML, phải mất cách tiếp cận rằng những gì nó thấy là những gì nó serializes, ngoại trừ việc nếu nó thấy bội, nó sẽ làm cho một mảng. Điều này là rất tốt cho nhiều cây DOM XML với bố trí nhất quán, nhưng tiếc là không thể làm việc cho các mục đích của bạn.

Bạn có thể xác minh điều này bằng cách xem nội dung cho các hàm SerializeGroupedNodes()SerializeNode() trong nguồn tệp sau.

XmlNodeConverter.cs source code @ CodePlex, ChangeSet #63616

Có một tùy chọn mà tôi trước đây đã nghĩ có thể là overkill nhưng sẽ rất hữu ích bây giờ mà chúng ta đều biết những gì mong đợi từ các hành vi mặc định trên vào cuối tuần tự.

Json.Net hỗ trợ sử dụng trình biến đổi tùy chỉnh bắt nguồn từ JsonConverter để ánh xạ các trường hợp giá trị cụ thể theo từng trường hợp cụ thể.

Chúng tôi có thể xử lý điều này ở bên tuần tự hoặc bên deserializing. Tôi đã chọn viết một giải pháp ở bên deserializing, vì bạn có thể có các lý do khác có sẵn để ánh xạ XML tới JSON.

Một lợi ích tuyệt vời là lớp học của bạn có thể vẫn nguyên vẹn, ngoại trừ ghi đè, yêu cầu bạn áp dụng thuộc tính. Dưới đây là một mẫu mã chứng tỏ làm thế nào để sử dụng JsonAttribute và một lớp chuyển đổi tùy chỉnh (MMArrayConverter) để khắc phục sự cố của bạn. Lưu ý rằng có thể bạn sẽ muốn thử nghiệm kỹ lưỡng hơn và có thể cập nhật trình chuyển đổi để xử lý các trường hợp khác, giả sử nếu bạn cuối cùng di chuyển đến IList<string> hoặc một số trường hợp sôi nổi khác như Lazy<List<string>> hoặc thậm chí làm cho nó hoạt động với generics.

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(); 
     } 
    } 
} 

gốc trả lời:

Nó sẽ là tốt nhất để sửa chữa JSON bạn đang nhận được tại nguồn, như nhiều người đã đã chỉ ra. Bạn có thể đăng một bản cập nhật cho thấy XML trong nhận xét cập nhật của bạn đang được ánh xạ tới JSON như thế nào, vì đó sẽ là tuyến đường tốt nhất tổng thể.

Tuy nhiên, nếu bạn thấy điều này là không thể và bạn muốn một số cách để sắp xếp và xử lý giá trị biến thể sau khi thực tế, bạn có thể vá mọi thứ bằng cách khai báo mm để nhập object và sau đó xử lý các trường hợp bạn sử dụng hỗ trợ LINQ của JSON.Net. Trong hai trường hợp bạn mô tả, bạn sẽ thấy rằng tuyên bố mm là loại object sẽ dẫn đến số null, một string hoặc JArray được chỉ định cho mm bằng cách gọi tới DeserializeObject<>.

Đây là mẫu mã hiển thị hành động này. Cũng có trường hợp trong các trường hợp khác mà bạn có thể nhận được JObject, cũng được đề cập trong mẫu này. Lưu ý rằng chức năng thành viên mmAsList() thực hiện công việc vá lỗi khác biệt cho bạn. Cũng xin lưu ý rằng tôi đã xử lý null tại đây bằng cách trả lại null cho List<string>; bạn có thể sẽ muốn sửa đổi điều này để thực hiện của bạn.

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

Giải pháp thú vị .. Cảm ơn bạn meklarian !! –

0

Trong trường hợp của bạn, bạn có thể trực tiếp sử dụng phương pháp tĩnh từ lớp JsonConvert

(thiết lập chuỗi giá trị, mục tiêu đối tượng, JsonSerializerSettings) PopulateObject;

vượt qua JsonSerializerSettings đối tượng như

new JsonSerializerSettings(){TypeNameHandling = TypeNameHandling.All}) 
Các vấn đề liên quan