我有一个多态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):
知道如何才能得到最终结果?
答案 0 :(得分:1)
首先,您希望摆脱ChngsType
中的PrevChng
,compInfo
等嵌套表格。您可以使用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" }