使用c#

时间:2015-09-25 13:07:17

标签: c# json

我想通常将一些json展平,以便我可以转换为数据表并使用c#绑定到数据网格

最好的办法是什么,记住我不知道我要下多少级别?

e.g。

{
  "appointmentid": 4,
  "policyid": 1,
  "guid": "00000000-0000-0000-0000-000000000000",
  "number": "1234567890",
  "ampm": "false",
  "date": "2015-09-08T00:00:00",
  "vehicle": {
    "id": 1,
    "guid": "00000000-0000-0000-0000-000000000000",
    "make": null,
    "model": null
  },
  "installer": {
    "installerid": "1",
    "name": "Installer 1",
    "contact": "qwerty",
    "qascore": "0",
    "address1": "qwerty",
    "address2": "qwerty",
    "address3": null,
    "address4": null,
    "city": "qwertyu",
    "county": "qwertyu",
    "postcode": "asdfghj",
    "country": "GB",
    "email": "asdfghj",
    "web": "asdfghjk",
    "archived": false
  },
  "installations": [
    {
      "installationid": 6,
      "installationstatus": {
        "installationstatusid": 4,
        "installationstatus": "FAIL"
      },
      "isactive": true
    },
    {
      "installationid": 7,
      "installationstatus": {
        "installationstatusid": 1,
        "installationstatus": "NEW"
      },
      "isactive": false
    }
  ],
  "archived": false
}

我想扩展这个(我想我可以迭代我转换它的数据表)而不是instal.1.installationid,我会得到installationid1。

因为我要在网格中显示结果数据表,我希望保持列名友好。

7 个答案:

答案 0 :(得分:35)

您可以使用Json.Net的LINQ-to-JSON API将数据解析为JToken结构。从那里,您可以使用递归辅助方法来遍历结构并将其展平为Dictionary<string, object>,其中键是原始JSON中每个值的“路径”。我会写这样的东西:

public class JsonHelper
{
    public static Dictionary<string, object> DeserializeAndFlatten(string json)
    {
        Dictionary<string, object> dict = new Dictionary<string, object>();
        JToken token = JToken.Parse(json);
        FillDictionaryFromJToken(dict, token, "");
        return dict;
    }

    private static void FillDictionaryFromJToken(Dictionary<string, object> dict, JToken token, string prefix)
    {
        switch (token.Type)
        {
            case JTokenType.Object:
                foreach (JProperty prop in token.Children<JProperty>())
                {
                    FillDictionaryFromJToken(dict, prop.Value, Join(prefix, prop.Name));
                }
                break;

            case JTokenType.Array:
                int index = 0;
                foreach (JToken value in token.Children())
                {
                    FillDictionaryFromJToken(dict, value, Join(prefix, index.ToString()));
                    index++;
                }
                break;

            default:
                dict.Add(prefix, ((JValue)token).Value);
                break;
        }
    }

    private static string Join(string prefix, string name)
    {
        return (string.IsNullOrEmpty(prefix) ? name : prefix + "." + name);
    }
}

将这个DeserializeAndFlatten方法与您的JSON一起使用,最终会得到如下的键值对:

appointmentid: 4
policyid: 1
guid: 00000000-0000-0000-0000-000000000000
number: 1234567890
ampm: false
date: 9/8/2015 12:00:00 AM
vehicle.id: 1
vehicle.guid: 00000000-0000-0000-0000-000000000000
vehicle.make:
vehicle.model:
installer.installerid: 1
installer.name: Installer 1
installer.contact: qwerty
installer.qascore: 0
installer.address1: qwerty
installer.address2: qwerty
installer.address3:
installer.address4:
installer.city: qwertyu
installer.county: qwertyu
installer.postcode: asdfghj
installer.country: GB
installer.email: asdfghj
installer.web: asdfghjk
installer.archived: False
installations.0.installationid: 6
installations.0.installationstatus.installationstatusid: 4
installations.0.installationstatus.installationstatus: FAIL
installations.0.isactive: True
installations.1.installationid: 7
installations.1.installationstatus.installationstatusid: 1
installations.1.installationstatus.installationstatus: NEW
installations.1.isactive: False
archived: False

如果您希望使按键更加人性化,可以使用一些字符串操作来剪切它们。也许是这样的:

var dict = JsonHelper.DeserializeAndFlatten(json);
foreach (var kvp in dict)
{
    int i = kvp.Key.LastIndexOf(".");
    string key = (i > -1 ? kvp.Key.Substring(i + 1) : kvp.Key);
    Match m = Regex.Match(kvp.Key, @"\.([0-9]+)\.");
    if (m.Success) key += m.Groups[1].Value;
    Console.WriteLine(key + ": " + kvp.Value);
}

这会给你这个输出:

appointmentid: 4
policyid: 1
guid: 00000000-0000-0000-0000-000000000000
number: 1234567890
ampm: false
date: 9/8/2015 12:00:00 AM
id: 1
guid: 00000000-0000-0000-0000-000000000000
make:
model:
installerid: 1
name: Installer 1
contact: qwerty
qascore: 0
address1: qwerty
address2: qwerty
address3:
address4:
city: qwertyu
county: qwertyu
postcode: asdfghj
country: GB
email: asdfghj
web: asdfghjk
archived: False
installationid0: 6
installationstatusid0: 4
installationstatus0: FAIL
isactive0: True
installationid1: 7
installationstatusid1: 1
installationstatus1: NEW
isactive1: False
archived: False

但是请注意,通过这种安排,你已经失去了一些上下文:例如,你可以看到现在有两个相同的archived键,而在原始的JSON中它们是不同的,因为它们出现在不同的部分层次结构(installer.archivedarchived)。你需要弄清楚如何自己处理这个问题。

小提琴:https://dotnetfiddle.net/gzhWHk

答案 1 :(得分:1)

反序列化,然后LINQ选择展平。我猜,因为你没有说明,你想要所有的预约和安装信息与特定安装在同一记录上吗?

我最初的想法是利用动态,这样你就可以避免为你的JSON输入静态模式。如果您已经拥有可以充当JSON模式的静态类型,那么您可以避免动态(以及所需的全部动态)。这是一个示例类 - 使用JSON.NET - 来说明我在想什么:

public class DeserializeAndFlatten
{
    public dynamic ParseJson()
    {
        var appointment = JObject.Parse(JsonData.JSON_TO_PARSE);  // <-- replace the constant w/ the real JSON...

        // this is where you flatten it all out!
        // not going to put all the fields in, that would kill the example, LOL
        var installations = appointment["installations"].Select(installation => new
        {
            appointmentId = appointment["appointmentid"],
            policyId = appointment["policyid"],

            vehicleId = appointment["vehicle"]["id"],
            vehicleMake = appointment["vehicle"]["make"],
            vehicleModel = appointment["vehicle"]["model"],

            installerId = appointment["installer"]["installerid"],
            installerName = appointment["installer"]["name"],

            installationId = installation["installationid"],
            installationStatus = installation["installationstatus"]["installationstatus"],
            installationStatusId = installation["installationstatus"]["installationstatusid"],
        }).ToList();

        return installations;
    }
}

您可以测试代码:

    static void Main(string[] args)
    {
        var jsonParser = new DeserializeAndFlatten();
        var installations = jsonParser.ParseJson();

        // FYI we get back a dynamic listing, 
        // so intellisense wont work...
        foreach (var installation in installations)
        {
            Console.WriteLine($"appointmentId: {installation.appointmentId}");
            Console.WriteLine($"installer: {installation.installerName}");
            Console.WriteLine($"installation id: {installation.installationId}");
            Console.WriteLine($"status: {installation.installationStatus}");
            Console.WriteLine();
        }

        Console.ReadLine();
    }

答案 2 :(得分:0)

还寻找解决方案,为我做了工作。将新的C#7 Tupel用于结果集。如果有人有另一个轻量级的解决方案,我很感兴趣:-)

async Task Main()
{
    var jsonUsers = await new HttpClient().GetStringAsync(@"https://jsonplaceholder.typicode.com/users");

    foreach (var flattedChild in GetFlatJsonChilds(JToken.Parse(jsonUsers)))
        Console.WriteLine($"{flattedChild.path}: {flattedChild.value}");
}

IEnumerable<(string path, string value)> GetFlatJsonChilds(JToken token)
{
    foreach (var child in token.Children())
    {   
        if(token.Type != JTokenType.Array &&token.Children().First().Type != JTokenType.Property && !child.Children().Any())
            yield return (child.Path, child.ToString());
        foreach(var childChild in GetFlatJsonChilds(child))
            yield return childChild;
    }
}

https://jsonplaceholder.typicode.com/users的结果:

[0].id: 1
[0].name: Leanne Graham
[0].username: Bret
[0].email: Sincere@april.biz
[0].address.street: Kulas Light
[0].address.suite: Apt. 556
[0].address.city: Gwenborough
[0].address.zipcode: 92998-3874
[0].address.geo.lat: -37.3159
[0].address.geo.lng: 81.1496
[0].phone: 1-770-736-8031 x56442
[0].website: hildegard.org
[0].company.name: Romaguera-Crona
[0].company.catchPhrase: Multi-layered client-server neural-net
[0].company.bs: harness real-time e-markets
[1].id: 2
[1].name: Ervin Howell
[1].username: Antonette
[1].email: Shanna@melissa.tv
[1].address.street: Victor Plains
[1].address.suite: Suite 879
[1].address.city: Wisokyburgh
[1].address.zipcode: 90566-7771
[1].address.geo.lat: -43.9509
[1].address.geo.lng: -34.4618
[1].phone: 010-692-6593 x09125
[1].website: anastasia.net
[1].company.name: Deckow-Crist
[1].company.catchPhrase: Proactive didactic contingency
[1].company.bs: synergize scalable supply-chains
[2].id: 3
[2].name: Clementine Bauch
[2].username: Samantha
[2].email: Nathan@yesenia.net
[2].address.street: Douglas Extension
[2].address.suite: Suite 847
[2].address.city: McKenziehaven
[2].address.zipcode: 59590-4157
[2].address.geo.lat: -68.6102
[2].address.geo.lng: -47.0653
[2].phone: 1-463-123-4447
[2].website: ramiro.info
[2].company.name: Romaguera-Jacobson
[2].company.catchPhrase: Face to face bifurcated interface
[2].company.bs: e-enable strategic applications
[3].id: 4
[3].name: Patricia Lebsack
[3].username: Karianne
[3].email: Julianne.OConner@kory.org
[3].address.street: Hoeger Mall
[3].address.suite: Apt. 692
[3].address.city: South Elvis
[3].address.zipcode: 53919-4257
[3].address.geo.lat: 29.4572
[3].address.geo.lng: -164.2990
[3].phone: 493-170-9623 x156
[3].website: kale.biz
[3].company.name: Robel-Corkery
[3].company.catchPhrase: Multi-tiered zero tolerance productivity
[3].company.bs: transition cutting-edge web services
[4].id: 5
[4].name: Chelsey Dietrich
[4].username: Kamren
[4].email: Lucio_Hettinger@annie.ca
[4].address.street: Skiles Walks
[4].address.suite: Suite 351
[4].address.city: Roscoeview
[4].address.zipcode: 33263
[4].address.geo.lat: -31.8129
[4].address.geo.lng: 62.5342
[4].phone: (254)954-1289
[4].website: demarco.info
[4].company.name: Keebler LLC
[4].company.catchPhrase: User-centric fault-tolerant solution
[4].company.bs: revolutionize end-to-end systems
[5].id: 6
[5].name: Mrs. Dennis Schulist
[5].username: Leopoldo_Corkery
[5].email: Karley_Dach@jasper.info
[5].address.street: Norberto Crossing
[5].address.suite: Apt. 950
[5].address.city: South Christy
[5].address.zipcode: 23505-1337
[5].address.geo.lat: -71.4197
[5].address.geo.lng: 71.7478
[5].phone: 1-477-935-8478 x6430
[5].website: ola.org
[5].company.name: Considine-Lockman
[5].company.catchPhrase: Synchronised bottom-line interface
[5].company.bs: e-enable innovative applications
[6].id: 7
[6].name: Kurtis Weissnat
[6].username: Elwyn.Skiles
[6].email: Telly.Hoeger@billy.biz
[6].address.street: Rex Trail
[6].address.suite: Suite 280
[6].address.city: Howemouth
[6].address.zipcode: 58804-1099
[6].address.geo.lat: 24.8918
[6].address.geo.lng: 21.8984
[6].phone: 210.067.6132
[6].website: elvis.io
[6].company.name: Johns Group
[6].company.catchPhrase: Configurable multimedia task-force
[6].company.bs: generate enterprise e-tailers
[7].id: 8
[7].name: Nicholas Runolfsdottir V
[7].username: Maxime_Nienow
[7].email: Sherwood@rosamond.me
[7].address.street: Ellsworth Summit
[7].address.suite: Suite 729
[7].address.city: Aliyaview
[7].address.zipcode: 45169
[7].address.geo.lat: -14.3990
[7].address.geo.lng: -120.7677
[7].phone: 586.493.6943 x140
[7].website: jacynthe.com
[7].company.name: Abernathy Group
[7].company.catchPhrase: Implemented secondary concept
[7].company.bs: e-enable extensible e-tailers
[8].id: 9
[8].name: Glenna Reichert
[8].username: Delphine
[8].email: Chaim_McDermott@dana.io
[8].address.street: Dayna Park
[8].address.suite: Suite 449
[8].address.city: Bartholomebury
[8].address.zipcode: 76495-3109
[8].address.geo.lat: 24.6463
[8].address.geo.lng: -168.8889
[8].phone: (775)976-6794 x41206
[8].website: conrad.com
[8].company.name: Yost and Sons
[8].company.catchPhrase: Switchable contextually-based project
[8].company.bs: aggregate real-time technologies
[9].id: 10
[9].name: Clementina DuBuque
[9].username: Moriah.Stanton
[9].email: Rey.Padberg@karina.biz
[9].address.street: Kattie Turnpike
[9].address.suite: Suite 198
[9].address.city: Lebsackbury
[9].address.zipcode: 31428-2261
[9].address.geo.lat: -38.2386
[9].address.geo.lng: 57.2232
[9].phone: 024-648-3804
[9].website: ambrose.net
[9].company.name: Hoeger LLC
[9].company.catchPhrase: Centralized empowering task-force
[9].company.bs: target end-to-end models

答案 3 :(得分:0)

使用库Json.Net,您可以使用JSONPath $..*获取JSON结构的所有成员,并过滤掉没有子对象的成员,以跳过容器属性。

例如

var schemaObject = JObject.Parse(schema);
var values = schemaObject
    .SelectTokens("$..*")
    .Where(t => !t.HasValues)
    .ToDictionary(t => t.Path, t => t.ToString());

答案 4 :(得分:0)

对于那些在F#中需要相同的人:

module JsonFlatten =

let Join prefix name =
    if String.IsNullOrEmpty(prefix) then name else prefix + "." + name

let rec FillDictionaryFromJToken (dict:Dictionary<string, string>) (token:JToken) (prefix:string) =
    match token.Type with
    | JTokenType.Object ->
        for prop in token.Children<JProperty>() do
            FillDictionaryFromJToken dict prop.Value (Join prefix prop.Name)
    | JTokenType.Array ->
        let mutable index = 0

        for value in token.Children() do
            FillDictionaryFromJToken dict  value (Join prefix (index.ToString()))
            index <- index + 1
    | _ ->
        dict.Add(prefix, sprintf "%A" (token :?> JValue).Value)

let DeserializeAndFlatten(json:string) =
    let dict = Dictionary<string, string>()
    let token = JToken.Parse(json);
    FillDictionaryFromJToken dict  token ""
    dict

答案 5 :(得分:0)

使用牛顿软件的Json.NET LINQ to JSON作为根对象的另一个变体(也可以使用JArray来完成):

var flattened = JObject.Parse(json)
    .Descendants()
    .OfType<JValue>()
    .ToDictionary(jv => jv.Path, jv => jv.ToString())

答案 6 :(得分:0)

这是使用Cinchoo ETL

来展平JSON /转换为DataTable的另一种方法

平面JSON:

using (var r = new ChoJSONReader("*** JSON file path ***"))
{
    foreach (var rec in r.Select(f => f.Flatten()))
        Console.WriteLine(rec.Dump());
}

JSON到数据表:

using (var r = new ChoJSONReader("*** JSON file path ***"))
{
    var dt = r.AsDataTable();
    Console.WriteLine(dt.DumpAsJson());
}