将 JSON 反序列化为对象时出错

问题描述 投票:0回答:8

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

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

错误是:

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

要修复此错误,请将 JSON 更改为 JSON 数组(例如 [1,2,3])或更改反序列化类型,使其成为普通的 .NET 类型(例如,不是像整数这样的原始类型,不是集合类型像数组或列表),可以从 JSON 对象反序列化。 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<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;
            }


        }
}
c# json json.net
8个回答
11
投票

这里真正的问题是您正在尝试反序列化为

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

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


1
投票

你要找的是

dynamic
类型。尽管不相关,this answer 包含很多关于如何迭代对象上不断变化的属性的信息。

您将需要添加一些额外的工作来弄清楚当结果是一个数组而不是单个对象时如何处理您的结果,正如您的错误向我们展示的那样。但是,这对您来说是一个很好的第一步。

基本上,

dynamic
对象是一个字典,很像 JSON 对象在 JavaScript 中的处理方式。您只需要遍历主对象中的每个 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);
}

这远非一个完整的示例,但我们没有足够的信息继续帮助您完成您正在尝试做的事情。


1
投票

您正在尝试反序列化为一个列表,但您的 JSON 实际上代表一个包含包含对象列表的数据属性的对象。这就是您收到此错误的原因。 Json.Net 无法将单个对象反序列化为列表。

请尝试这个:创建一个包含数据类型 Object 的单个属性的类,并将此类传递给反序列化。

class Parent
{
    public object Data { get; set;}
}

然后像这样反序列化:

var output = JsonConvert.DeserializeObject<Parent>(jsonData);

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

0
投票

如果你的数据是动态的,那么试试动态列表:

using System.Web.Script.Serialization;

JavaScriptSerializer jss = new JavaScriptSerializer();
var d=jss.Deserialize<dynamic>(str);

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

0
投票

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

然而,我使用的是二进制版本,这仍然存在问题——我进行了一个测试,你可以在其中配置使用二进制或 json,json 版本工作得很好,但二进制抱怨没有得到数组类型.

我开始为此使用 BsonDataReader,并查看它的属性和方法以了解如何最好地查看内容,你瞧,它有一个名为:

reader.ReadRootValueAsArray 

将其设置为“真”就可以了。


0
投票

这对我有用
为根元素创建一个类

public class JsonRoot{public List<Animal> Animmals { get; set; }}

并通过访问对象反序列化
List <Animal> deserializedObject = JsonConvert.DeserializeObject<JsonRoot>  (jsonString).Animmals;

© www.soinside.com 2019 - 2024. All rights reserved.