Vic*_*nja 5 .net c# json json.net
我正在使用Newtonsoft库将JSON字符串解析为相应的.NET对象.我在解析作为数组的JSON属性时遇到问题.有时JSON属性是一个数组,有时候,它是一个单独的元素.
例:
这是.NET对象:
public class xx
{
public string yy { get; set; }
public List<string> mm{ get; set; }
}
Run Code Online (Sandbox Code Playgroud)
当我收到这个JSON时:
{ "xx": {"yy":"nn", "mm": [ "zzz", "aaa" ] } }
Run Code Online (Sandbox Code Playgroud)
我完全可以做到:
JsonConvert.DeserializeObject<xx>(json);
Run Code Online (Sandbox Code Playgroud)
但有时我收到这个JSON:
{ "xx": {"yy":"nn", "mm":"zzz"} }
Run Code Online (Sandbox Code Playgroud)
由于C#对象上的list属性,反序列化失败.
如何定义一个对象以反序列化同一对象中的两个JSON字符串(with List<string>).
--------更新-----
首先,WS生成一个XML做一些操作.. XML就像
<xx yy='nn'><mm>zzz</mm></xx>
Run Code Online (Sandbox Code Playgroud)
如果有更多的元素是:
<xx yy='nn'><mm>zzz</mm><mm>aaa</mm></xx>
Run Code Online (Sandbox Code Playgroud)
最后,WS将此XML转换为:
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
var json = JsonConvert.SerializeXmlNode(doc);
Run Code Online (Sandbox Code Playgroud)
并发送给我json ..并在这里开始我的问题..
更新答案:
看看JSON.Net如何映射XML,它采用的方法是它看到的是它序列化的内容,除非它看到倍数,它将构成一个数组.这对于许多具有一致布局的XML DOM树来说非常有用,但遗憾的是它不能用于您的目的.
您可以通过查看函数的正文SerializeGroupedNodes()以及SerializeNode()以下文件源来验证这一点.
XmlNodeConverter.cs源代码@ 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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
原答案:
正如许多人已经指出的那样,最好修复你在源头接收的JSON.您可能希望发布一个更新,显示更新后的注释中的XML如何映射到JSON,因为这将是整体的最佳路径.
但是,如果您发现这是不可能的,并且您希望某种方式在事后序列化和处理变量值,则可以通过声明mm为类型来修补object,然后使用JSON.Net自行处理可能的情况. Linq的支持.在您描述的两种情况中,您会发现声明mm为类型object将导致通过调用分配a null,a string或a .JArraymmDeserializeObject<>
这是一个代码示例,显示了这一点.在其他情况下,您也可以收到一个案例JObject,本示例中也包含了该案例.请注意,成员函数mmAsList()可以为您修补差异.还要注意我在null这里通过返回nullfor 来处理List<string>; 您可能希望修改此实现.
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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
发送服务发送的内容应该符合合同。如果没有,那么好吧,要么你殴打发送的开发人员并让他们修复它,要么发送给你的各种东西都是合同。遗憾的是,您没有任何元数据可以确定,您只需尝试各种合同,直到其中一个有效。
object someValue;
try
{
someValue =JsonConvert.DeserializeObject<TypeWithList>(json);
}
catch
{
try
{
someValue = JsonConvert.DeserializeObject<TypeWithString>(json);
}
catch
{
//Darn, yet another type
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
5495 次 |
| 最近记录: |