我有一个多态的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
}
]
}
}
}
}
]
}
}
有人曾经帮助我使用扩展类,以便我可以检索 ADP 代码。 下面是扩展类:
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()
一些注意事项:
不能使用索引器来选择深度嵌套的令牌,就像尝试使用表达式
o["SecuritySymbol.symbolset.name"]
那样。 索引器将仅返回直接子级。 您需要使用SelectToken()
来选择孙子孙女。"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
并返回IEnumerable<JToken>
。 如果是数组,它将返回数组,否则它将返回(假定的)单个令牌的数组。 这将为您提供一致的界面,无论是数组还是单个对象。
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"],
};