LINQ to JSON - 来自不同阵列的设置列表

时间:2016-07-20 19:16:25

标签: c# linq c#-4.0 json.net linq-to-json

我有一个多态json字符串。这是它的样子:

{
    "Product" : {
        "Context" : {
            "IssuerDetails" : {
                "Issuer" : {
                    "@clientCode" : "BMTEST-CA",
                    "@companyName" : "zTest BM Company, Inc",
                    "@companyId" : "1",
                    "IssuerChanges" : [{
                            "@type" : "Book Value",
                            "@previous" : "$9.06",
                            "@current" : "$55.34"
                        }, {
                            "@type" : "Price Target",
                            "@previous" : "$50.00",
                            "@current" : "$199.00"
                        }, {
                            "@type" : "EPS",
                            "@previous" : "2.10",
                            "@current" : "2.09",
                            "@period" : "5",
                            "@year" : "2017"
                        }, {
                            "@type" : "Income Tax",
                            "@previous" : "56",
                            "@current" : "55",
                            "@period" : "5",
                            "@year" : "2015"
                        }
                    ],
                    "SecurityDetails" : {
                        "Security" : {
                            "@primaryIndicator" : "Yes",
                            "Clusters" : [{
                                    "@name" : "Company Data",
                                    "@rank" : "2",
                                    "FinancialValue" : [{
                                            "@financialsType" : "Dividend",
                                            "CurrentValue" : {
                                                "@displayValue" : "$5.02",
                                            }
                                        }, {
                                            "@financialsType" : "Book Value",
                                            "CurrentValue" : {
                                                "@displayValue" : "$55.34",
                                            },
                                            "PreviousValue" : {
                                                "@displayValue" : "$9.06",
                                                "@type" : "INCREASE",
                                            }
                                        }
                                    ]
                                }, {
                                    "@rank" : "1",
                                    "@name" : "AAPL & Market Data",
                                    "FinancialValue" : [{
                                            "@financialsType" : "Rating",
                                            "@shortCode" : "Mkt",
                                            "CurrentValue" : {
                                                "@displayValue" : "Market Perform",
                                            }
                                        }, {
                                            "@financialsType" : "Rating Qualifier",
                                            "CurrentValue" : {
                                                "@displayValue" : "Speculative",
                                            }
                                        }
                                    ]
                                }
                            ]
                        }
                    }
                }
            }
        }
    }
}

我使用以下扩展程序类:

public static class JsonExtensions
{
    public static IEnumerable<JObject> ObjectsOrSelf(this JToken root)
    {
        if (root is JObject)
            yield return (JObject)root;
        else if (root is JContainer)
            foreach (var item in ((JContainer)root).Children())
                foreach (var child in item.ObjectsOrSelf())
                    yield return child;
        else
            yield break;
    }
}

基于此,这是我的疑问:

JObject feed = JObject.Parse(jsonText);

var compInfo = from issuer in feed.SelectTokens("Product.Context.IssuerDetails.Issuer").SelectMany(i => i.ObjectsOrSelf())
       let issuerChanges = issuer.SelectTokens("IssuerChanges").SelectMany(s => s.ObjectsOrSelf())
       where issuerChanges != null 
       let finValues = issuer.SelectTokens("SecurityDetails.Security.Clusters").SelectMany(s => s.ObjectsOrSelf())
       where finValues != null
       select new
        {
           Id = (int)issuer["@companyId"], 
           BMOTicker = (string)issuer["@clientCode"],
           CompName = (string)issuer["@companyName"],
           ChngsType = issuerChanges.Select(c => (string)c["@type"]),
           PrevChng = issuerChanges.Select(c => (string)c["@previous"]),
           CurrChng = issuerChanges.Select(c => (string)c["@current"]),
           Period = issuerChanges.Select(c => (string)c["@period"]),
           Year = issuerChanges.Select(c => (string)c["@year"]),
           FinValueName = finValues.Select(c => (string)c["@financialsType"])
    };

当我尝试查询时,我没有收到错误但得到以下内容(来自LinqPad): enter image description here

最终结果应如下所示(在Excel中模拟): enter image description here

知道如何才能得到最终结果?

1 个答案:

答案 0 :(得分:1)

首先,您希望摆脱ChngsType中的PrevChngcompInfo等嵌套表格。您可以使用SelectMany()(流利语法)或循环from关键字(查询语法),如下例所示:

var compInfoTest1 =
    from issuer in feed.SelectTokens("Product.Context.IssuerDetails.Issuer")
    from change in issuer["IssuerChanges"]
    select new { Company = issuer["@companyName"], Type = change["@type"] };

您可以看到compInfoTest1包含一个扁平的公司类型表:

{ Company = {zTest BM Company, Inc}, Type = {Book Value} }
{ Company = {zTest BM Company, Inc}, Type = {Price Target} }
{ Company = {zTest BM Company, Inc}, Type = {EPS} }
{ Company = {zTest BM Company, Inc}, Type = {Income Tax} }

请注意,执行此操作不需要ObjectsOrSelf()扩展方法。

第二次ObjectsOrSelf()SecurityDetails的双嵌套结构上失败(对象 - &gt;数组 - &gt;对象 - &gt;数组 - &gt;对象 - 它在第二个对象级别停止,因此无法到达@financialsType)。要解决这个问题,您需要使用相同的展平技术重复from以获得下一个内部级别:

var compInfoTest2 =
    from issuer in feed.SelectTokens("Product.Context.IssuerDetails.Issuer")
    from cluster in issuer["SecurityDetails"]["Security"]["Clusters"]
    from finVal in cluster["FinancialValue"]
    select new { Company = issuer["@companyName"], FinType = finVal["@financialsType"] };

以下是查询结果:

{ Company = {zTest BM Company, Inc}, FinType = {Dividend} }
{ Company = {zTest BM Company, Inc}, FinType = {Book Value} }
{ Company = {zTest BM Company, Inc}, FinType = {Rating} }
{ Company = {zTest BM Company, Inc}, FinType = {Rating Qualifier} }

第三,因为FinType / Value对与其他数据之间没有任何关系,您无法写出任何条件来匹配change中的项目finVal套。相反,您可以使用Enumerable.Zip()粘合&#34;对应的&#34;项目

所以最终查询是:

var compInfo =
    from issuer in feed.SelectTokens("Product.Context.IssuerDetails.Issuer")
    let finValues = (
        from cluster in issuer["SecurityDetails"]["Security"]["Clusters"]
        from finVal in cluster["FinancialValue"]
        select new {
            FinValueName = (string)finVal["@financialsType"],
            Value = (string)finVal["CurrentValue"]["@displayValue"],
        }
    )
    from changesAndFinValues in issuer["IssuerChanges"].Zip(finValues, (c,f) => new {
            ChngsType = (string)c["@type"],
            PrevChng = (string)c["@previous"],
            CurrChng = (string)c["@current"],
            Period = (string)c["@period"],
            Year = (string)c["@year"],
            f.FinValueName,
            f.Value
    })
    select new
    {
        Id = (int)issuer["@companyId"],
        BMOTicker = (string)issuer["@clientCode"],
        CompName = (string)issuer["@companyName"],
        changesAndFinValues.ChngsType,
        changesAndFinValues.PrevChng,
        changesAndFinValues.CurrChng,
        changesAndFinValues.Period,
        changesAndFinValues.Year,
        changesAndFinValues.FinValueName,
        changesAndFinValues.Value,
    };

它会对您的样本数据产生以下结果:

{ Id = 1, BMOTicker = "BMTEST-CA", CompName = "zTest BM Company, Inc", ChngsType = "Book Value", PrevChng = "$9.06", CurrChng = "$55.34", Period = null, Year = null, FinValueName = "Dividend", Value = "$5.02" }
{ Id = 1, BMOTicker = "BMTEST-CA", CompName = "zTest BM Company, Inc", ChngsType = "Price Target", PrevChng = "$50.00", CurrChng = "$199.00", Period = null, Year = null, FinValueName = "Book Value", Value = "$55.34" }
{ Id = 1, BMOTicker = "BMTEST-CA", CompName = "zTest BM Company, Inc", ChngsType = "EPS", PrevChng = "2.10", CurrChng = "2.09", Period = "5", Year = "2017", FinValueName = "Rating", Value = "Market Perform" }
{ Id = 1, BMOTicker = "BMTEST-CA", CompName = "zTest BM Company, Inc", ChngsType = "Income Tax", PrevChng = "56", CurrChng = "55", Period = "5", Year = "2015", FinValueName = "Rating Qualifier", Value = "Speculative" }