2011-11-10 46 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})