【问题标题】:How to deserialize a JSON array into an object using Json.Net?如何使用 Json.Net 将 JSON 数组反序列化为对象?
【发布时间】:2017-11-29 15:22:39
【问题描述】:

我有一个有效的 JSON 对象,其中包含一个 JSON 数组。 JSON 数组没有大括号,其中包含一个逗号分隔的混合类型列表。它看起来像这样:

{
    "ID": 17,
    "Days": 979,
    "Start_Date": "10/13/2012",
    "End_Date": "11/12/2012",
    "State": "",
    "Page": 1,
    "Test": "Valid",
    "ROWS": [
        [441210, "A", "12/31/2009", "OK", "Done", "KELLEY and Co'", "12/31/2009", "06/29/2010", "TEXAS", "Lawyers", 6, "", "<img src=\"/includes/images/Icon_done.gif\" border=\"0\" alt=\"Done\" title=\"Done\" />"],
        [441151, "B", "12/31/2009", "No", "In-process", "Sage & Co", "12/31/2009", "06/29/2010", "CALIFORNIA", "Realtor", 6, "", "<img src=\"/includes/images/Icon_InProcess.gif\" border=\"0\" alt=\"In Process\" title=\"In Process\" />"]
    ]
}

我创建了一个类来反映 JSON 结构,其中包含一个复杂数组的列表:

class myModel
{
    public int ID { get; set; }
    public int Days { get; set; }
    public DateTime StartDate { get; set; }
    public DateTime EndDate { get; set; }
    public string State { get; set; }
    public string Page { get; set; }
    public string Test { get; set; }
    List<ChildModel> Rows { get; set; }
}

我也用 List of a List 对其进行了测试:

List<List<ChildModel>> Rows { get; set; }

还有这样的子模型:

class ChildModel
{
    public int ID { get; set; }
    public string StatusId { get; set; }
    public DateTime ContactDate { get; set; }
    public string State { get; set; }
    public string Status { get; set; }
    public string CustomerName { get; set; }
    public DateTime WorkStartDate { get; set; }
    public DateTime WorkEndDate { get; set; }
    public string Territory { get; set; }
    public string CustType { get; set; }
    public int JobOrder { get; set; }
    public string Filler { get; set; }
    public string Link { get; set; }
}

在我的 program.cs 文件中,我像这样反序列化它:

using (StreamReader r = new StreamReader(@"D:\01.json"))
{
    myModel items = JsonConvert.DeserializeObject<myModel>(r.ReadToEnd());
}

当我运行这个程序时,子对象(行)总是null。我做错了什么?

【问题讨论】:

  • 所以你是说ChildModel 被序列化为一个数组而不是一个对象?序列化是否超出您的控制范围?
  • 我猜这是因为您的列表中的内容与您的 ChildModel 类之间没有映射。反序列化器如何知道将什么值放入模型的哪个属性中?如果可能,请修复序列化,否则构建自定义解析器并祈祷上帝不要更改列表中数据的顺序。
  • 要将该数组反序列化为ChildModel,您可以使用C# JSON.NET - Deserialize response that uses an unusual data structure 中的ObjectToArrayConverter&lt;ChildModel&gt;
  • @EdPlunkett,是的,我无法控制该序列化。我只使用供应商提供的服务。

标签: c# json json.net


【解决方案1】:

Json.Net 没有自动将数组映射到类的功能。为此,您需要自定义JsonConverter。这是一个适合您的通用转换器。它使用自定义[JsonArrayIndex] 属性来标识类中的哪些属性对应于数组中的哪些索引。如果 JSON 发生变化,这将允许您轻松更新模型。此外,您可以安全地从类中省略不需要的属性,例如 Filler

代码如下:

public class JsonArrayIndexAttribute : Attribute
{
    public int Index { get; private set; }
    public JsonArrayIndexAttribute(int index)
    {
        Index = index;
    }
}

public class ArrayToObjectConverter<T> : JsonConverter where T : class, new()
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(T);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JArray array = JArray.Load(reader);

        var propsByIndex = typeof(T).GetProperties()
            .Where(p => p.CanRead && p.CanWrite && p.GetCustomAttribute<JsonArrayIndexAttribute>() != null)
            .ToDictionary(p => p.GetCustomAttribute<JsonArrayIndexAttribute>().Index);

        JObject obj = new JObject(array
            .Select((jt, i) =>
            {
                PropertyInfo prop;
                return propsByIndex.TryGetValue(i, out prop) ? new JProperty(prop.Name, jt) : null;
            })
            .Where(jp => jp != null)
        );

        T target = new T();
        serializer.Populate(obj.CreateReader(), target);

        return target;
    }

    public override bool CanWrite
    {
        get { return false; }
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

要使用转换器,您需要标记您的ChildModel 类,如下所示:

[JsonConverter(typeof(ArrayToObjectConverter<ChildModel>))]
class ChildModel
{
    [JsonArrayIndex(0)]
    public int ID { get; set; }
    [JsonArrayIndex(1)]
    public string StatusId { get; set; }
    [JsonArrayIndex(2)]
    public DateTime ContactDate { get; set; }
    [JsonArrayIndex(3)]
    public string State { get; set; }
    [JsonArrayIndex(4)]
    public string Status { get; set; }
    [JsonArrayIndex(5)]
    public string CustomerName { get; set; }
    [JsonArrayIndex(6)]
    public DateTime WorkStartDate { get; set; }
    [JsonArrayIndex(7)]
    public DateTime WorkEndDate { get; set; }
    [JsonArrayIndex(8)]
    public string Territory { get; set; }
    [JsonArrayIndex(9)]
    public string CustType { get; set; }
    [JsonArrayIndex(10)]
    public int JobOrder { get; set; }
    [JsonArrayIndex(12)]
    public string Link { get; set; }
}

然后像往常一样反序列化,它应该可以按您的意愿工作。这是一个演示:https://dotnetfiddle.net/n3oE3L

注意:我没有实现WriteJson,所以如果你把你的模型序列化回JSON,它不会序列化回数组格式;相反,它将使用默认的对象序列化。

【讨论】:

  • 这个答案很好用。但是,请注意它很慢,可能是由于反射代码。在我对 253118837 条记录的解析中,这段代码花了 80 分钟。如果性能是一个问题,请考虑放弃强类型并使用动态和索引,例如dynamic items = JsonConvert.DeserializeObject(r.ReadToEnd()); var id = items.ROWS[0]; ...。该代码将我的 80 分钟查询缩短到 11 分钟。
【解决方案2】:

使用它来将您的数据反序列化为 json 对象...

public class Rootobject
{
    public int ID { get; set; }
    public int Days { get; set; }
    public string Start_Date { get; set; }
    public string End_Date { get; set; }
    public string State { get; set; }
    public int Page { get; set; }
    public string Test { get; set; }
    public object[][] ROWS { get; set; }
}

...然后创建一个实用函数,将object[][] 转换为您想要的目标对象...

(使用 Visual Studio 创建:编辑 -> 选择性粘贴 -> JSON 类)

【讨论】:

    猜你喜欢
    • 2013-08-14
    • 1970-01-01
    • 1970-01-01
    • 2013-07-25
    • 2016-06-28
    • 1970-01-01
    • 2011-05-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多