首先使用 C# Json.NET 对具有变量名称的 Json 数组进行反序列化



我从人口普查局的公共API中获得了一个不规则的JSON数组。变量名称都在第一个元素中,我在反序列化它时遇到了问题。

http://api.census.gov/data/2014/pep/agesex?get=AGE,POP,&for=us:*&DATE=7

给我这样的JSON:

[["AGE","POP","SEX","DATE","us"],
["0","3948350","0","7","1"],
["1","3962123","0","7","1"],
["2","3957772","0","7","1"],
["3","4005190","0","7","1"],
["4","4003448","0","7","1"],
["5","4004858","0","7","1"],
["6","4134352","0","7","1"],
["7","4154000","0","7","1"]]

我可以使用以下方法成功反序列化它:

var test1 = JsonConvert.DeserializeObject<String[][]>(jsonStr);

但是,我正在尝试将其反序列化为这样的类:

public class TestClass
{
    public string AGE { get; set; }
    public string POP { get; set; }
    public string SEX { get; set; }
    public string DATE { get; set; }
    public string us { get; set; }
}

我正在尝试这样做:

var test2 = JsonConvert.DeserializeObject<TestClass[]>(jsonStr);

但是我遇到了以下异常:

类型为"Newtonsoft.Json.JsonSerializationException"的异常 发生在 Newtonsoft.Json 中.dll但未在用户代码中处理

其他信息:无法创建和填充列表类型 测试类。路径"[0]",第 1 行,位置 阿拉伯数字。

这有两个

部分。

首先是将 JSON 转换为 C# 中可用的数据,其次是将数据转换为漂亮的对象。

下面是以下代码的工作 dotNetFiddle.net 示例:https://dotnetfiddle.net/Cr0aRL

JSON 中的每一行都由一个字符串数组组成。所以这是一个字符串数组的数组。在 C# 中,可以写成字符串 [][]。

因此,要使用 JSON.Net 将 JSON 转换为可用数据,您可以执行以下操作:

    var json = "[["AGE","POP","SEX","DATE","us"],["0","3948350","0","7","1"],["1","3962123","0","7","1"],["2","3957772","0","7","1"],["3","4005190","0","7","1"],["4","4003448","0","7","1"],["5","4004858","0","7","1"],["6","4134352","0","7","1"],["7","4154000","0","7","1"]]";
    var rawData = JsonConvert.DeserializeObject<string[][]>(json);

接下来是将数据转换为对象。

第一行是标题,包含列名,所以我们要抓取它,然后找出每个列名的列索引。

    var headerRow = rawData.First();    
    var ageIndex = Array.IndexOf(headerRow, "AGE");
    var popIndex = Array.IndexOf(headerRow, "POP");
    var sexIndex = Array.IndexOf(headerRow, "SEX");
    var dateIndex = Array.IndexOf(headerRow, "DATE");
    var usIndex = Array.IndexOf(headerRow, "us");

现在我们有了索引,我们需要获取每一行,并将其转换为适当的对象。为此,我使用了 LINQ,因为它非常擅长以清晰的方式表示数据处理。

    var testData = rawData
        .Skip(1) //The first row is a header, not data
        .Select(dataRow => new TestClass()
        {
            AGE = dataRow[ageIndex],
            POP = dataRow[popIndex],
            SEX = dataRow[sexIndex],
            DATE = dataRow[dateIndex],
            us = dataRow[usIndex]
        });

最后进行一些测试,以确保您拥有所需的数据。

    //Get the second data row as an example
    var example = testData.Skip(1).First();
    //Output example POP to check value
    Console.WriteLine(example.POP);

以上所有内容都是非常手动的。

您必须知道所需的标题,然后手动查找索引,然后手动将行映射到对象。

对于一个简单的用例来说,这样做很有可能很好。但在更大和/或更复杂的系统中,您可能希望/需要自动执行这些步骤。

自动化这些步骤是可能的,但超出了本答案的范围,因为您如何处理它可能取决于许多不同的因素。

您可以创建自定义 JsonConverter 来处理反序列化期间的此转换。 转换代码实际上与此处的其他答案没有太大区别,除了它被封装到一个单独的类中,这样您就不会将主代码与转换详细信息混淆。 从您的主代码的角度来看,它"只是工作"。

以下是编写转换器的方法:

public class TestClassArrayConverter : JsonConverter 
{
    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(TestClass[]));
    }
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JArray table = JArray.Load(reader);
        TestClass[] items = new TestClass[table.Count - 1];
        for (int i = 1; i < table.Count; i++)
        {
            JArray row = (JArray)table[i];
            items[i - 1] = new TestClass
            {
                AGE = (string)row[0],
                POP = (string)row[1],
                SEX = (string)row[2],
                DATE = (string)row[3],
                us = (string)row[4]
            };
        }
        return items;
    }
    public override bool CanWrite
    {
        get { return false; }
    }
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

以下是您将如何使用它:

var test2 = JsonConvert.DeserializeObject<TestClass[]>(jsonStr, new TestClassArrayConverter());

小提琴:https://dotnetfiddle.net/68Q0KT

您必须自己进行处理,因为 json 反序列化程序无法知道如何将值放入 respecitve 变量中。

如果你知道,这将是这个结构,例如你可以添加一个适当的构造函数

public TestClass(string[] values) {
    AGE = values[0]; 
    ...
}

到你的班级。然后将结果序列化为字符串数组数组,然后将内部数组传递给构造函数。

var t1 = JsonConvert.DeserializeObject<string[][]>(jsonStr);
//skip the first entry, as this contains the headers
var t2 = t1.Skip(1).Select(x=> new TestClass(x));

如果结构不同,则必须编写一些更复杂的映射代码。

您必须执行一些自定义映射,因为您的 Json 没有任何命名约定,因此您必须处理数组和索引格式的数据。这将起作用:

var jsonStr = "[["AGE","POP","SEX","DATE","us"], ["0","3948350","0","7","1"], ["1","3962123","0","7","1"], ["2","3957772","0","7","1"], ["3","4005190","0","7","1"], ["4","4003448","0","7","1"], ["5","4004858","0","7","1"], ["6","4134352","0","7","1"], ["7","4154000","0","7","1"]]";
var test2 = JsonConvert.DeserializeObject<string[][]>(jsonStr);
var test3 = test2.Select(x => new TestClass()
{
    AGE = x[0].ToString(),
    POP = x[1].ToString(),
    SEX = x[2].ToString(),
    DATE = x[3].ToString(),
    us = x[4].ToString()
}).ToList();
//test Case
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
namespace ApiController.Test
{
    [TestClass]
    public class DownloadIrregularJsonStringObjects
    {
        string ApiKey => "YourPersonalCensusKey";
        /// <summary>
        /// You have to get your own ApiKey from the Census Website
        /// </summary>       
        [TestMethod]
        public void TestGetItem()
        {
        string url = $"http://api.census.gov/data/timeseries/healthins/sahie?get=NIC_PT,NAME,NUI_PT&for=county:*&in=state:*&time=2015&key={YourPersonalCensusKey}";
        string expected = "Autauga County, AL";
        IList<HealthData> actual = ApiController.DownloadIrregularJsonStringObjects.GetCensusHealthData(url);
        Assert.AreEqual(actual[0].NAME, expected);
    }
}
}
///Actual Assembly
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
namespace ApiController
{
   public  class DownloadIrregularJsonStringObjects
    {
        public static IList<HealthData> GetCensusHealthData(string url)
        {
            var json = GetData(url);
            var rawData = JsonConvert.DeserializeObject<string[][]>(json);
            var headerRow = rawData.First();
            var nic_pt_Index = Array.IndexOf(headerRow, "NIC_PT");
            var name_Index = Array.IndexOf(headerRow, "NAME");
            var nui_pt_Index = Array.IndexOf(headerRow, "NUI_PT");
            IList<HealthData> retVal = new List<HealthData>();
            foreach (var r in rawData.Skip(1))
            {
                HealthData dataRow = new HealthData();
                dataRow.NIC_PT = r[nic_pt_Index];
                dataRow.NAME = r[name_Index];
                dataRow.NUI_PT = r[nui_pt_Index];
                retVal.Add(dataRow);                
            }
            return retVal;
        }
    private static string GetData(string url)
    {
        using (var w = new WebClient())
        {
            var jsonData = string.Empty;
            jsonData = w.DownloadString(url);
            return jsonData;
        }
    }
}
public class HealthData
{
    public string NIC_PT { get; set; }
    public string NAME { get; set; }
    public string NUI_PT { get; set; }       
}
}

相关内容

  • 没有找到相关文章

最新更新