我需要转换从REST API获取的JSON数据,并将它们转换为CSV以进行某些分析。问题是JSON数据不一定遵循相同的内容,因此我无法定义映射类型。这已经成为一项挑战,占用了我太多的时间。我已经创建了一些代码,但当然它没有工作,因为它在这一行引发异常
var data = JsonConvert.DeserializeObject<List<object>>(jsonData);
错误是:
其他信息:无法反序列化当前的JSON对象 (例如{&#34; name&#34;:&#34; value&#34;})进入类型 &#39; System.Collections.Generic.List`1 [System.Object的]&#39;因为类型 需要一个JSON数组(例如[1,2,3])才能正确反序列化。
要修复此错误,请将JSON更改为JSON数组(例如[1,2,3])或更改反序列化类型,使其成为普通的.NET类型(例如,不是像整数这样的基本类型,而不是可以从JSON对象反序列化的集合类型,如数组或List)。 JsonObjectAttribute也可以添加到类型中以强制它从JSON对象反序列化。
路径&#39;数据&#39;,第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<object>>(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<T>( IList<T> 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;
}
}
}
答案 0 :(得分:3)
这里真正的问题是你试图反序列化为List<object>
但你的JSON实际上代表一个包含data
属性的对象,然后它包含一个对象列表。这就是你得到这个错误的原因。 Json.Net无法将单个对象反序列化为列表。我认为你真正想做的是定义一个像这样的容器类:
class Root
{
public List<Dictionary<string, object>> Data { get; set;}
}
然后像这样反序列化:
var data = JsonConvert.DeserializeObject<Root>(jsonData).Data;
然后,您将得到一个字典列表,其中每个字典代表JSON数组中的一个项目。字典键值对是每个项目中的动态值。然后,您可以像使用任何其他字典一样使用这些。例如,以下是如何转储所有数据:
foreach (var dict in data)
{
foreach (var kvp in dict)
{
Console.WriteLine(kvp.Key + ": " + kvp.Value);
}
Console.WriteLine();
}
答案 1 :(得分:1)
如果您的数据是动态的,请尝试使用动态列表:
using System.Web.Script.Serialization;
JavaScriptSerializer jss = new JavaScriptSerializer();
var d=jss.Deserialize<dynamic>(str);
答案 2 :(得分:1)
您要找的是dynamic
类型。虽然不相关,this answer包含有关如何迭代对象中不断变化的属性的大部分信息。
当您的错误显示我们时,您需要添加一些额外的工作来确定如何处理结果,当它是数组而不是单个对象时。但是,这对你来说是一个很好的第一步。
基本上,dynamic
对象是一个Dictionary,就像在JavaScript中处理JSON对象一样。您只需遍历主对象中的每个KeyValuePair对象并遍历其属性。
var data = JsonConvert.DeserializeObject<dynamic>(jsonData);
var rows = new List<string>();
// Go through the overall object, and get each item in
// the array, or property in a single object.
foreach (KeyValuePair<string, object> 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<string, object> prop in obj)
{
// Very dummy way to demo adding to a CSV
string += prop.Value.ToString() + ",";
}
rows.Add(string);
}
这远不是一个完整的例子,但我们没有足够的信息来帮助你完成你想要做的事情。
答案 3 :(得分:0)
尝试使用此类而不是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<Datum> data { get; set; }
}
更改为:
var data = JsonConvert.DeserializeObject<RootObject>(jsonData);
答案 4 :(得分:0)
由于您尝试将对象类型反序列化为列表类型,因此不会直接对其进行解除序列化。
你可以这样做:
var data = JsonConvert.DeserializeObject<ObjectDataList>(jsonData);
var rows = new List<DeserializedData>();
foreach (dynamic item in data)
{
var newData = new DeserializedData();
foreach (dynamic prop in item)
{
var row = new KeyValuePair<string, string>
(prop.Name.ToString(), prop.Value.ToString());
newData.Add(row);
}
rows.Add(newData);
}
以下是新课程
//class for key value type data
class DeserializedData
{
List<KeyValuePair<string, string>> NewData =
new List<KeyValuePair<string, string>>();
internal void Add(KeyValuePair<string, string> row)
{
NewData.Add(row);
}
}
[DataContract]
class ObjectDataList
{
[DataMember(Name ="data")]
List<object> Data { get; set; }
public IEnumerator<object> GetEnumerator()
{
foreach (var d in Data)
{
yield return d;
}
}
}
答案 5 :(得分:0)
据我所知,更新版本的Newtonsoft现在可以实现这一点,无需额外的工作。
我正在使用二进制版本,但这仍然有问题 - 我有一个测试,你可以配置使用二进制或json,json版本工作得很好,但二进制文件抱怨没有得到一个数组类型。
我开始使用BsonDataReader,并查看它上面的属性和方法,看看我如何最好地查看内容,并且看,这有一个叫做的属性:
reader.ReadRootValueAsArray
将此设置为'true'就可以了。
答案 6 :(得分:0)
您正在尝试反序列化为List,但是JSON实际上代表一个包含数据属性的单个对象,该数据属性包含对象列表。这就是为什么您会收到此错误。 Json.Net不能将单个对象反序列化为列表。
请尝试以下操作:创建一个数据类型为Object的包含单个属性的类,并将该类传递给反序列化。
class Parent
{
public object Data { get; set;}
}
然后像这样反序列化:
var output = JsonConvert.DeserializeObject<Parent>(jsonData);