将JSON反序列化为Object时出错

我需要转换从REST API获取的JSON数据,并将它们转换为CSV以进行某些分析。 问题是JSON数据不一定遵循相同的内容,因此我无法定义映射类型。 这已经成为一项挑战,占用了我太多的时间。 我已经创建了一些代码,但当然它不起作用,因为它会抛出exception

var data = JsonConvert.DeserializeObject<List>(jsonData); 

错误是:

附加信息:无法将当前JSON对象(例如{“name”:“value”})反序列化为类型’System.Collections.Generic.List`1 [System.Object]’,因为该类型需要JSON数组(例如[1] ,2,3]正确反序列化。

要修复此错误,请将JSON更改为JSON数组(例如[1,2,3])或更改反序列化类型,使其成为普通的.NET类型(例如,不是像整数这样的基本类型,而不是类似的集合类型可以从JSON对象反序列化的数组或List。 JsonObjectAttribute也可以添加到类型中以强制它从JSON对象反序列化。

路径’数据’,第2行,第10位。

请让我知道我能做些什么才能实现这一目标。

数据样本就是这样,数据字段可以经常更改,例如可以在第二天添加新字段,因此我没有自由创建.Net类来映射数据。

 { "data": [ { "ID": "5367ab140026875f70677ab277501bfa", "name": "Happiness Initiatives - Flow of Communication/Process & Efficiency", "objCode": "PROJ", "percentComplete": 100.0, "plannedCompletionDate": "2014-08-22T17:00:00:000-0400", "plannedStartDate": "2014-05-05T09:00:00:000-0400", "priority": 1, "projectedCompletionDate": "2014-12-05T08:10:21:555-0500", "status": "CPL" }, { "ID": "555f452900c8b845238716dd033cf71b", "name": "UX Personalization Think Tank and Product Strategy", "objCode": "PROJ", "percentComplete": 0.0, "plannedCompletionDate": "2015-12-01T09:00:00:000-0500", "plannedStartDate": "2015-05-22T09:00:00:000-0400", "priority": 1, "projectedCompletionDate": "2016-01-04T09:00:00:000-0500", "status": "APR" }, { "ID": "528b92020051ab208aef09a4740b1fe9", "name": "SCL Health System - full Sitecore implementation (Task groups with SOW totals in Planned hours - do not bill time here)", "objCode": "PROJ", "percentComplete": 100.0, "plannedCompletionDate": "2016-04-08T17:00:00:000-0400", "plannedStartDate": "2013-11-04T09:00:00:000-0500", "priority": 1, "projectedCompletionDate": "2013-12-12T22:30:00:000-0500", "status": "CPL" } ] } namespace BusinessLogic { public class JsonToCsv { public string ToCsv(string jsonData, string datasetName) { var data = JsonConvert.DeserializeObject<List>(jsonData); DataTable table = ToDataTable(data); StringBuilder result = new StringBuilder(); for (int i = 0; i < table.Columns.Count; i++) { result.Append(table.Columns[i].ColumnName); result.Append(i == table.Columns.Count - 1 ? "\n" : ","); } foreach (DataRow row in table.Rows) { for (int i = 0; i < table.Columns.Count; i++) { result.Append(row[i].ToString()); result.Append(i == table.Columns.Count - 1 ? "\n" : ","); } } return result.ToString().TrimEnd(new char[] {'\r', '\n'}); } private DataTable ToDataTable( IList data ) { PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(T)); DataTable table = new DataTable(); for (int i = 0 ; i < props.Count ; i++) { PropertyDescriptor prop = props[i]; table.Columns.Add(prop.Name, prop.PropertyType); } object[] values = new object[props.Count]; foreach (T item in data) { for (int i = 0 ; i < values.Length ; i++) { values[i] = props[i].GetValue(item); } table.Rows.Add(values); } return table; } } } 

这里真正的问题是您正在尝试反序列化为List但您的JSON实际上表示包含data属性的单个对象,该属性随后包含对象列表。 这就是你收到这个错误的原因。 Json.Net无法将单个对象反序列化为列表。 我认为你真正想做的是定义一个像这样的容器类:

 class Root { public List> Data { get; set;} } 

然后像这样反序列化:

 var data = JsonConvert.DeserializeObject(jsonData).Data; 

然后,您将得到一个字典列表,其中每个字典代表JSON数组中的一个项目。 字典键值对是每个项目中的动态值。 然后,您可以像使用任何其他字典一样使用这些。 例如,以下是如何转储所有数据:

 foreach (var dict in data) { foreach (var kvp in dict) { Console.WriteLine(kvp.Key + ": " + kvp.Value); } Console.WriteLine(); } 

小提琴: https : //dotnetfiddle.net/6UaKhJ

如果您的数据是动态的,请尝试动态列表:

 using System.Web.Script.Serialization; JavaScriptSerializer jss = new JavaScriptSerializer(); var d=jss.Deserialize(str); 

您正在寻找的是dynamic类型。 虽然不相关,但这个答案包含了很多关于如何能够迭代对象上不断变化的属性的信息。

当错误显示我们时,您需要添加一些额外的工作来确定如何在数组与单个对象时处理结果。 但是,这对您来说是一个很好的第一步。

基本上, dynamic对象是一个Dictionary,就像在JavaScript中处理JSON对象一样。 您只需遍历主对象中的每个KeyValuePair对象并遍历其属性即可。

 var data = JsonConvert.DeserializeObject(jsonData); var rows = new List(); // Go through the overall object, and get each item in // the array, or property in a single object. foreach (KeyValuePair item in data) { dynamic obj = item.Value; var row = ""; // Perhaps add a check here to see if there are more // properties (if it is an item in an array). If not // then you are working with a single object, and each // item is a property itself. foreach (KeyValuePair prop in obj) { // Very dummy way to demo adding to a CSV string += prop.Value.ToString() + ","; } rows.Add(string); } 

这远不是一个完整的例子,但我们没有足够的信息来帮助你完成你想要做的事情。

尝试使用此类而不是Object

 public class Datum { public string ID { get; set; } public string name { get; set; } public string objCode { get; set; } public double percentComplete { get; set; } public string plannedCompletionDate { get; set; } public string plannedStartDate { get; set; } public int priority { get; set; } public string projectedCompletionDate { get; set; } public string status { get; set; } } public class RootObject { public List data { get; set; } } 

改为:

 var data = JsonConvert.DeserializeObject(jsonData); 

由于您尝试将对象类型反序列化为列表类型,因此不会直接对其进行反序列化。

你可以这样做:

 var data = JsonConvert.DeserializeObject(jsonData); var rows = new List(); foreach (dynamic item in data) { var newData = new DeserializedData(); foreach (dynamic prop in item) { var row = new KeyValuePair (prop.Name.ToString(), prop.Value.ToString()); newData.Add(row); } rows.Add(newData); } 

这是新课程

 //class for key value type data class DeserializedData { List> NewData = new List>(); internal void Add(KeyValuePair row) { NewData.Add(row); } } [DataContract] class ObjectDataList { [DataMember(Name ="data")] List Data { get; set; } public IEnumerator GetEnumerator() { foreach (var d in Data) { yield return d; } } } 

据我所知,更新版本的Newtonsoft现在可以实现这一点,无需额外的工作。

我正在使用二进制版本,但这仍然有问题 – 我有一个测试,你可以配置使用二进制或json,并且json版本工作得很好,但二进制文件抱怨没有获得数组类型。

我开始使用BsonDataReader,并查看它上面的属性和方法,看看我如何最好地查看内容,并且看,这有一个属性叫做:

 reader.ReadRootValueAsArray 

将此设置为“true”就可以了。