LINQ to Json 从多态 json 检索数据
LINQ to Json data retrieval from polymorphic json
我有一个多态 json 字符串。这是它的样子:
{
"?xml":{
"@version":"1.0",
"@encoding":"UTF-8"
},
"DataFeed":{
"@FeedName":"AdminData",
"Issuer":[
{
"name":"Apple",
"symbol":"AAPL-O",
"active":"1",
"securities":{
"Security":{
"sedol":"B0XXF09",
"coverage":{
"Coverage":{
"analyst":{
"@firstName":"Steve",
"@lastName":"Jobs",
"@rank":"1"
}
}
},
"symbolMappingList":{
"SecuritySymbol":{
"symbolset":{
"id":"11",
"symbol":"ISIN",
"name":"ISIN",
"rixmlName":"ISIN",
"researchDirect":"S&P"
},
"symbol":"US44919P5XXX"
}
},
"symbolMapping":{
"entry":{
"int":"11",
"SecuritySymbol":{
"@reference":"../../../symbolMappingList/SecuritySymbol"
}
}
},
"customFields":{
"customField":[
{
"@name":"ADP",
"@type":"Textbox",
"values":{
"value":"H0192XX"
}
},
{
"@name":"Top 15",
"@type":"Dropdown, multiple choice",
"values":null
}
]
}
}
}
},
{
"name":"Microsoft",
"symbol":"MSFT-OTC",
"active":"1",
"securities":{
"Security":{
"sedol":"B8FW54",
"coverage":{
"Coverage":{
"analyst":{
"@firstName":"Bill",
"@lastName":"Gates",
"@rank":"1"
}
}
},
"symbolMappingList":{
"SecuritySymbol":[
{
"symbolset":{
"id":"3",
"symbol":"CUSIP",
"name":"CUSIP",
"rixmlName":"CUSIP",
"researchDirect":"S&P"
},
"symbol":"04316A1XX"
},
{
"symbolset":{
"id":"11",
"symbol":"ISIN",
"name":"ISIN",
"rixmlName":"ISIN",
"researchDirect":"S&P"
},
"symbol":"US04316A10XX"
}
]
},
"symbolMapping":{
"entry":[
{
"int":"3",
"SecuritySymbol":{
"@reference":"../../../symbolMappingList/SecuritySymbol"
}
},
{
"int":"11",
"SecuritySymbol":{
"@reference":"../../../symbolMappingList/SecuritySymbol[2]"
}
}
]
},
"customFields":{
"customField":[
{
"@name":"ADP Security Code",
"@type":"Textbox",
"values":null
},
{
"@name":"Top 15",
"@type":"Dropdown, multiple choice",
"values":null
}
]
}
}
}
}
]
}
}
有人曾经帮助我扩展 class 以便我可以检索 ADP 代码。这是扩展名 class:
public static class JsonExtensions
{
public static IEnumerable<JToken> DescendantsAndSelf(this JToken node)
{
if (node == null)
return Enumerable.Empty<JToken>();
var container = node as JContainer;
if (container != null)
return container.DescendantsAndSelf();
else
return new[] { node };
}
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;
}
}
基于此,这是我的查询:
var compInfo = from issuer in feed.SelectTokens("DataFeed.Issuer").SelectMany(i => i.ObjectsOrSelf())
where (string)issuer["active"] == "1"
let security = issuer.SelectTokens("securities.Security").SelectMany(s => s.ObjectsOrSelf()).FirstOrDefault()
let securitySymbol = issuer.SelectTokens("securities.Security.symbolMappingList")
.SelectMany(s => s.ObjectsOrSelf()).FirstOrDefault()
where security != null
select new
{
CompName = (string)issuer["name"],
SEDOL = ((string)security["sedol"]).StartsWith("0") ?
String.Format("'{0}", (string)security["sedol"]) : (string)security["sedol"],
ADP = security["customFields"]
.DescendantsAndSelf()
.OfType<JObject>()
.Where(o => (string)o["@name"] == "ADP Security Code")
.Select(o => (string)o.SelectToken("values.value"))
.FirstOrDefault(),
ISIN = security["symbolMappingList"]
.DescendantsAndSelf()
.OfType<JObject>()
.Where(o => (string)o["SecuritySymbol.symbolset.name"] == "ISIN")
.Select(o => (string)o.SelectToken("SecuritySymbol.symbol"))
.FirstOrDefault()
};
我可以获取 ADP 代码。但是我怎样才能得到 ISIN 代码呢?我认为我非常接近,但我得到了所有空值。我需要更改什么才能使这项工作正常进行?
应该是:
ISIN = security["symbolMappingList"]
.DescendantsAndSelf()
.OfType<JObject>()
.Where(o => (string)o.SelectToken("symbolset.name") == "ISIN")
.Select(o => (string)o.SelectToken("symbol"))
.FirstOrDefault()
一些注意事项:
您不能使用 indexers to select deeply nested tokens, as you try to do with the expression o["SecuritySymbol.symbolset.name"]
. Indexers will only return immediate children. You need to use SelectToken()
给 select 孙子。
"SecuritySymbol"
的值属性有时是一个对象:
"SecuritySymbol":{
"symbolset":{
有时是一个数组:
"SecuritySymbol":[
{
"symbolset":{
取决于其中的项目数。由于这种多态性,你不能只做 SelectToken("SecuritySymbol.symbolset.name")
。而是使用 DescendantsAndSelf()
递归搜索 SecuritySymbol
及其后代(无论是否嵌入数组中)的值,以查找具有适当 "symbolset.name"
子标记的对象。
这是一个更新后的查询,它经过了稍微优化,以消除对 SelectToken
的重复调用,并过滤 "sedol"
名称:
var filterString = "B0XXF09"; // Null if filtering is not desired
var compInfo = from issuer in feed.SelectTokens("DataFeed.Issuer").SelectMany(i => i.ObjectsOrSelf())
where (string)issuer["active"] == "1"
let security = issuer.SelectTokens("securities.Security").SelectMany(s => s.ObjectsOrSelf()).FirstOrDefault()
where security != null
let sedol = (string)security["sedol"]
where (sedol != null && filterString == null || sedol.Contains(filterString))
select new
{
CompName = (string)issuer["name"],
SEDOL = sedol.StartsWith("0") ? String.Format("'{0}", sedol) : sedol,
ADP = security["customFields"]
.DescendantsAndSelf()
.OfType<JObject>()
.Where(o => (string)o["@name"] == "ADP Security Code")
.Select(o => (string)o.SelectToken("values.value"))
.FirstOrDefault(),
ISIN = security["symbolMappingList"]
.DescendantsAndSelf()
.OfType<JObject>()
.Where(o => (string)o.SelectToken("symbolset.name") == "ISIN")
.Select(o => (string)o.SelectToken("symbol"))
.FirstOrDefault()
};
创建一个扩展方法,它将采用 JToken
并将 return 和 IEnumerable<JToken>
。如果是数组,它将 return 数组,否则它将 return (假设的)单个标记的数组。这将为您提供一致的界面,无论是数组还是单个对象。
public static IEnumerable<JToken> SingleOrMultiple(this JToken source)
{
IEnumerable<JToken> arr = source as JArray;
return arr ?? new[] { source };
}
然后相应地构建您的查询。
var query =
from issuer in obj.SelectTokens("DataFeed.Issuer[*]")
where (int)issuer["active"] == 1
let security = issuer.SelectToken("securities.Security") as JObject
where security != null
let sedol = (string)security["sedol"]
let customFields = JObject.FromObject(
security.SelectTokens("customFields.customField[*]")
.ToDictionary(
o => (string)o["@name"],
o => (string)o.SelectToken("values.value")
)
)
let symbolMapping = JObject.FromObject(
security.SelectToken("symbolMappingList.SecuritySymbol").SingleOrMultiple()
.ToDictionary(
o => (string)o.SelectToken("symbolset.name"),
o => (string)o.SelectToken("symbolset.symbol")
)
)
select new
{
CompanyName = (string)issuer["name"],
Sedol = sedol.StartsWith("0") ? $"'{sedol}" : sedol,
Adp = customFields["ADP"],
Isin = symbolMapping["ISIN"],
};
我有一个多态 json 字符串。这是它的样子:
{
"?xml":{
"@version":"1.0",
"@encoding":"UTF-8"
},
"DataFeed":{
"@FeedName":"AdminData",
"Issuer":[
{
"name":"Apple",
"symbol":"AAPL-O",
"active":"1",
"securities":{
"Security":{
"sedol":"B0XXF09",
"coverage":{
"Coverage":{
"analyst":{
"@firstName":"Steve",
"@lastName":"Jobs",
"@rank":"1"
}
}
},
"symbolMappingList":{
"SecuritySymbol":{
"symbolset":{
"id":"11",
"symbol":"ISIN",
"name":"ISIN",
"rixmlName":"ISIN",
"researchDirect":"S&P"
},
"symbol":"US44919P5XXX"
}
},
"symbolMapping":{
"entry":{
"int":"11",
"SecuritySymbol":{
"@reference":"../../../symbolMappingList/SecuritySymbol"
}
}
},
"customFields":{
"customField":[
{
"@name":"ADP",
"@type":"Textbox",
"values":{
"value":"H0192XX"
}
},
{
"@name":"Top 15",
"@type":"Dropdown, multiple choice",
"values":null
}
]
}
}
}
},
{
"name":"Microsoft",
"symbol":"MSFT-OTC",
"active":"1",
"securities":{
"Security":{
"sedol":"B8FW54",
"coverage":{
"Coverage":{
"analyst":{
"@firstName":"Bill",
"@lastName":"Gates",
"@rank":"1"
}
}
},
"symbolMappingList":{
"SecuritySymbol":[
{
"symbolset":{
"id":"3",
"symbol":"CUSIP",
"name":"CUSIP",
"rixmlName":"CUSIP",
"researchDirect":"S&P"
},
"symbol":"04316A1XX"
},
{
"symbolset":{
"id":"11",
"symbol":"ISIN",
"name":"ISIN",
"rixmlName":"ISIN",
"researchDirect":"S&P"
},
"symbol":"US04316A10XX"
}
]
},
"symbolMapping":{
"entry":[
{
"int":"3",
"SecuritySymbol":{
"@reference":"../../../symbolMappingList/SecuritySymbol"
}
},
{
"int":"11",
"SecuritySymbol":{
"@reference":"../../../symbolMappingList/SecuritySymbol[2]"
}
}
]
},
"customFields":{
"customField":[
{
"@name":"ADP Security Code",
"@type":"Textbox",
"values":null
},
{
"@name":"Top 15",
"@type":"Dropdown, multiple choice",
"values":null
}
]
}
}
}
}
]
}
}
有人曾经帮助我扩展 class 以便我可以检索 ADP 代码。这是扩展名 class:
public static class JsonExtensions
{
public static IEnumerable<JToken> DescendantsAndSelf(this JToken node)
{
if (node == null)
return Enumerable.Empty<JToken>();
var container = node as JContainer;
if (container != null)
return container.DescendantsAndSelf();
else
return new[] { node };
}
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;
}
}
基于此,这是我的查询:
var compInfo = from issuer in feed.SelectTokens("DataFeed.Issuer").SelectMany(i => i.ObjectsOrSelf())
where (string)issuer["active"] == "1"
let security = issuer.SelectTokens("securities.Security").SelectMany(s => s.ObjectsOrSelf()).FirstOrDefault()
let securitySymbol = issuer.SelectTokens("securities.Security.symbolMappingList")
.SelectMany(s => s.ObjectsOrSelf()).FirstOrDefault()
where security != null
select new
{
CompName = (string)issuer["name"],
SEDOL = ((string)security["sedol"]).StartsWith("0") ?
String.Format("'{0}", (string)security["sedol"]) : (string)security["sedol"],
ADP = security["customFields"]
.DescendantsAndSelf()
.OfType<JObject>()
.Where(o => (string)o["@name"] == "ADP Security Code")
.Select(o => (string)o.SelectToken("values.value"))
.FirstOrDefault(),
ISIN = security["symbolMappingList"]
.DescendantsAndSelf()
.OfType<JObject>()
.Where(o => (string)o["SecuritySymbol.symbolset.name"] == "ISIN")
.Select(o => (string)o.SelectToken("SecuritySymbol.symbol"))
.FirstOrDefault()
};
我可以获取 ADP 代码。但是我怎样才能得到 ISIN 代码呢?我认为我非常接近,但我得到了所有空值。我需要更改什么才能使这项工作正常进行?
应该是:
ISIN = security["symbolMappingList"]
.DescendantsAndSelf()
.OfType<JObject>()
.Where(o => (string)o.SelectToken("symbolset.name") == "ISIN")
.Select(o => (string)o.SelectToken("symbol"))
.FirstOrDefault()
一些注意事项:
您不能使用 indexers to select deeply nested tokens, as you try to do with the expression
o["SecuritySymbol.symbolset.name"]
. Indexers will only return immediate children. You need to useSelectToken()
给 select 孙子。"SecuritySymbol"
的值属性有时是一个对象:"SecuritySymbol":{ "symbolset":{
有时是一个数组:
"SecuritySymbol":[ { "symbolset":{
取决于其中的项目数。由于这种多态性,你不能只做
SelectToken("SecuritySymbol.symbolset.name")
。而是使用DescendantsAndSelf()
递归搜索SecuritySymbol
及其后代(无论是否嵌入数组中)的值,以查找具有适当"symbolset.name"
子标记的对象。
这是一个更新后的查询,它经过了稍微优化,以消除对 SelectToken
的重复调用,并过滤 "sedol"
名称:
var filterString = "B0XXF09"; // Null if filtering is not desired
var compInfo = from issuer in feed.SelectTokens("DataFeed.Issuer").SelectMany(i => i.ObjectsOrSelf())
where (string)issuer["active"] == "1"
let security = issuer.SelectTokens("securities.Security").SelectMany(s => s.ObjectsOrSelf()).FirstOrDefault()
where security != null
let sedol = (string)security["sedol"]
where (sedol != null && filterString == null || sedol.Contains(filterString))
select new
{
CompName = (string)issuer["name"],
SEDOL = sedol.StartsWith("0") ? String.Format("'{0}", sedol) : sedol,
ADP = security["customFields"]
.DescendantsAndSelf()
.OfType<JObject>()
.Where(o => (string)o["@name"] == "ADP Security Code")
.Select(o => (string)o.SelectToken("values.value"))
.FirstOrDefault(),
ISIN = security["symbolMappingList"]
.DescendantsAndSelf()
.OfType<JObject>()
.Where(o => (string)o.SelectToken("symbolset.name") == "ISIN")
.Select(o => (string)o.SelectToken("symbol"))
.FirstOrDefault()
};
创建一个扩展方法,它将采用 JToken
并将 return 和 IEnumerable<JToken>
。如果是数组,它将 return 数组,否则它将 return (假设的)单个标记的数组。这将为您提供一致的界面,无论是数组还是单个对象。
public static IEnumerable<JToken> SingleOrMultiple(this JToken source)
{
IEnumerable<JToken> arr = source as JArray;
return arr ?? new[] { source };
}
然后相应地构建您的查询。
var query =
from issuer in obj.SelectTokens("DataFeed.Issuer[*]")
where (int)issuer["active"] == 1
let security = issuer.SelectToken("securities.Security") as JObject
where security != null
let sedol = (string)security["sedol"]
let customFields = JObject.FromObject(
security.SelectTokens("customFields.customField[*]")
.ToDictionary(
o => (string)o["@name"],
o => (string)o.SelectToken("values.value")
)
)
let symbolMapping = JObject.FromObject(
security.SelectToken("symbolMappingList.SecuritySymbol").SingleOrMultiple()
.ToDictionary(
o => (string)o.SelectToken("symbolset.name"),
o => (string)o.SelectToken("symbolset.symbol")
)
)
select new
{
CompanyName = (string)issuer["name"],
Sedol = sedol.StartsWith("0") ? $"'{sedol}" : sedol,
Adp = customFields["ADP"],
Isin = symbolMapping["ISIN"],
};