我有一个多态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代码?我觉得我非常接近,但我得到的都是空的。我需要做些什么才能使其发挥作用?
答案 0 :(得分:1)
应该是:
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"]
时,无法使用indexers来选择深层嵌套的标记。索引者只会返回直系孩子。您需要使用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()
};
答案 1 :(得分:0)
创建一个扩展方法,该方法将占用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"],
};