【问题标题】:Deserialize JSON String into DataTable / DataSet using c#使用 c# 将 JSON 字符串反序列化为 DataTable / DataSet
【发布时间】:2014-11-17 10:40:53
【问题描述】:

我有以下 JSON 字符串

{
"1":[
        {"cityId":93,"cityName":"Tapah","cityCode":"TAP"},
        {"cityId":3,"cityName":"Melaka","cityCode":"MLK"},
        {"cityId":6,"cityName":"Kota Bharu","cityCode":"KB"},
        {"cityId":7,"cityName":"Dungun","cityCode":"DG"}
    ],
"2":[
        {"cityId":77,"cityName":"Grik","cityCode":"GRIK"},
        {"cityId":6,"cityName":"Kota Bharu","cityCode":"KB"},
        {"cityId":7,"cityName":"Dungun","cityCode":"DG"},
        {"cityId":98,"cityName":"Bangan Serai","cityCode":"BS"}
    ],
"6":[
        {"cityId":3,"cityName":"Melaka","cityCode":"MLK"},
        {"cityId":82,"cityName":"Teluk Intan","cityCode":"TI"},
        {"cityId":7,"cityName":"Dungun","cityCode":"DG"}
    ]
}

当我使用 JSON.NET 时

DataSet obj = JsonConvert.DeserializeObject(responseBody);

我只得到以下表格

cityId  |  cityName   | cityCode
-----------------------------------
93           Tapah          TAP
3            Melaka         MLK
6            Kota Bharu     KB
7            Dungun         DG

我需要喜欢这个

from  |    cityId  |  cityName   | cityCode
-----------------------------------------------
 1            93           Tapah          TAP
 1            3            Melaka         MLK
 1            6            Kota Bharu     KB
 1            7            Dungun         DG
 2            77           Grik           GRIK
 2            6            Kota Bharu     KB
 2            7            Dungun         DG
 2            98           Bangan Serai   BS
 6            3            Melaka         MLK
 6            82           Teluk Intan    TI
 6            7            Dungun         DG             

【问题讨论】:

    标签: c# json json.net


    【解决方案1】:

    问题的原因是JsonConvert.DeserializeObject 的不正确使用在这种情况下应该可以工作:

    使用列详细信息定义类型/架构(如上面的响应所示)

    public class City
    {
        public int cityId;
        public string cityName;
        public string cityCode;
    }
    

    现在你在响应正文中得到的是类型,Json 默认是键值对

    Dictionary<object, List<City>>
    

    现在你的反序列化代码应该是:

    var obj = JsonConvert.DeserializeObject<Dictionary<object, List<City>>>(responseBody);
    

    obj 的类型为Dictionary&lt;object, List&lt;City&gt;&gt;

    现在创建自定义数据集对象的架构,如下所示:

    public class CustomDataset
        {
            public object from;
            public int cityId;
            public string cityName;
            public string cityCode;
        }
    

    使用简单的 foreach 从 obj 类型创建自定义数据集,如下所示:

    List<CustomDataset> cdList = new List<CustomDataset> ();
    
    foreach(object key in obj.Keys)
    {
    foreach(City c in obj[key])
    {
      cdList.Add(key, c.cityId, c.cityName, cityCode);
    }
    }
    

    List&lt;CustomDataset&gt; 类型的 cdList 将是您返回的包含您需要的解决方案。如果使用Linq,可以跳过使用Foreach的最后部分,这很简单:

    List<CustomDataset> cdList = 
                                  obj.SelectMany(x => x.Value.Select(y =>
                                                   new CustomDataset
                                                   {
                                                    from = x.Key,
                                                    cityId = y.cityId,
                                                    cityName = y.cityName,
                                                    cityCode =  y.cityCode
                                                   }
                                                   )).ToList();
    

    【讨论】:

    • 为什么将object 用于TKey?如果您使用 int 类型,JSON.NET 足够聪明,可以调用 Convert.ToInt32()
    • @Steven Liekens 你是对的,我只是将实现通用化
    • @CnuVas 欢迎,请参考最近的编辑,我已经更正了 Linq 查询,现在这将获取您期望的结果。 Linq 针对 foreach 循环提供了一个优雅的解决方案
    【解决方案2】:

    我建议将 JSON 对象反序列化为语义等价的框架类型。在您的情况下,JSON 在语义上等同于 IDictionary&lt;TKey, ICollection&lt;TValue&gt;&gt;&gt;

    IDictionary<int, ICollection<City>> cities;
    cities = JsonConvert.DeserializeObject<IDictionary<int, ICollection<City>>>(responseBody);
    

    然后,自己进行DataSet 转换,而不是依赖反射。

    var dataSet = new DataSet();
    var dataTable = dataSet.Tables.Add();
    dataTable.Columns.Add("from");
    dataTable.Columns.Add("cityId");
    dataTable.Columns.Add("cityName");
    dataTable.Columns.Add("cityCode");
    
    // foreach KeyValuePair in IDictionary<TKey, ICollection<TValue>>
    foreach (var cityGroup in cities)
    {
        // foreach TValue in ICollection<TValue>
        foreach (var city in cityGroup.Value)
        {
            dataTable.LoadDataRow(
                new object[]
                {
                    cityGroup.Key,
                    city.cityId,
                    city.cityName,
                    city.cityCode
                },
                LoadOption.PreserveChanges);
        }
    }
    

    对于TValue,我使用了一个简单的数据契约类。

    public class City
    {
        public int cityId;
        public string cityName;
        public string cityCode;
    }
    

    【讨论】:

    • 非常感谢史蒂文·利肯斯
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-15
    相关资源
    最近更新 更多