这是我之前发布的帖子的后续内容,在这里: Querying and Filtering Array of JObjects with Linq
我在这里发布了一个dotnetfiddle: https://dotnetfiddle.net/s75wGu
道歉,但我无法弄清楚为什么我在dotnetfiddle中的System.Collections.Generic.IEnumerable上收到错误,但该程序在Linqpad和Visual Studio下工作。
源JSON如下所示:
{
"Object": {
"NM": "Name1",
"AA": "Val01",
"BB": "Val02",
"CC": "Val03",
"DD": "Val04",
"EE": "Val05",
"FF": "Val06",
"GG": "Val07",
"HH": "Val08",
"Object": [
{
"NM": "Name2",
"AA": "Val01",
"BB": "Val02",
"CC": "Val03",
"DD": "Val04",
"EE": "Val05",
"FF": "Val06",
"GG": "Val07",
"HH": "Val08",
"Object": [
{
"NM": "Name3",
"AA": "Val01",
"BB": "Val02",
"CC": "Val03",
"DD": "Val04",
"EE": "Val05",
"FF": "Val06",
"GG": "Val07",
"HH": "Val08"
},
{
"NM": "Name4",
"AA": "Val01",
"BB": "Val02",
"CC": "Val03",
"DD": "Val04",
"EE": "Val05",
"FF": "Val06",
"GG": "Val07",
"HH": "Val08"
},
{
"NM": "Name5",
"AA": "Val01",
"BB": "Val02",
"CC": "Val03",
"DD": "Val04",
"EE": "Val05",
"FF": "Val06",
"GG": "Val07",
"HH": "Val08"
}
]
}
]
}
}
如您所见,代码旨在查询JSON文档并将其展平为List<JObject>
对象。
if (jo is JObject)
子句应该是直截了当的:如果Linq迭代找到一个JObject对象,它会创建一个joWork
JObject实例,向其添加属性,并返回单个joWork
对象。这很好。
但是我的问题是使用if (jo is JArray)
子句:在这里,我认为Linq希望我返回一个JObject对象,但是我有一个JObject数组,我想作为单独的JObject返回。
我的dotnetfiddle代码产生的输出是:
joWork-Object: {
"_Parent": "",
"NM": "Name1",
"AA": "Val01",
"BB": "Val02",
"CC": "Val03",
"DD": "Val04",
"EE": "Val05",
"FF": "Val06",
"GG": "Val07",
"HH": "Val08"
}
joWork-Array: {
"_Parent": "Name1",
"_Group": 1,
"NM": "Name2",
"AA": "Val01",
"BB": "Val02",
"CC": "Val03",
"DD": "Val04",
"EE": "Val05",
"FF": "Val06",
"GG": "Val07",
"HH": "Val08"
}
joWork:
joWork-Array: {
"_Parent": "Name2",
"_Group": 2,
"NM": "Name5",
"AA": "Val01",
"BB": "Val02",
"CC": "Val03",
"DD": "Val04",
"EE": "Val05",
"FF": "Val06",
"GG": "Val07",
"HH": "Val08"
}
joWork:
joWork:
joWork:
请注意,我们只有对象名称(NM)Name1,Name2,然后是Name5。我想要的是Name1,Name2,Name3,Name4和Name5--每个都有一个包含父对象的NM属性的_Parent属性。
我意识到我只获得Name5,因为我的Linq查询中的JArray if语句只返回数组中的最后一个元素。我想返回数组中的所有元素。最终输出应如下所示:
joWork-Object: {
"_Parent": "",
"NM": "Name1",
"AA": "Val01",
"BB": "Val02",
"CC": "Val03",
"DD": "Val04",
"EE": "Val05",
"FF": "Val06",
"GG": "Val07",
"HH": "Val08"
}
joWork-Array: {
"_Parent": "Name1",
"_Group": 1,
"NM": "Name2",
"AA": "Val01",
"BB": "Val02",
"CC": "Val03",
"DD": "Val04",
"EE": "Val05",
"FF": "Val06",
"GG": "Val07",
"HH": "Val08"
}
joWork-Array: {
"_Parent": "Name2",
"_Group": 2,
"NM": "Name3",
"AA": "Val01",
"BB": "Val02",
"CC": "Val03",
"DD": "Val04",
"EE": "Val05",
"FF": "Val06",
"GG": "Val07",
"HH": "Val08"
}
joWork-Array: {
"_Parent": "Name2",
"_Group": 2,
"NM": "Name4",
"AA": "Val01",
"BB": "Val02",
"CC": "Val03",
"DD": "Val04",
"EE": "Val05",
"FF": "Val06",
"GG": "Val07",
"HH": "Val08"
}
joWork-Array: {
"_Parent": "Name2",
"_Group": 2,
"NM": "Name5",
"AA": "Val01",
"BB": "Val02",
"CC": "Val03",
"DD": "Val04",
"EE": "Val05",
"FF": "Val06",
"GG": "Val07",
"HH": "Val08"
}
答案 0 :(得分:1)
您可以使用Enumerable.SelectMany()
代替Enumerable.Select()
来返回linq查询中的多个对象,将返回的内部枚举展平为整个枚举的组成对象:
var root = (JContainer)JToken.Parse(jsonFromFile);
var controlRoot = (JContainer)root["Object"];
string parentKey = "";
int groupId = 0;
var query =
controlRoot
.DescendantsAndSelf()
.Where(jt => (jt.Type == JTokenType.Object) || (jt.Type == JTokenType.Array))
.SelectMany(jo =>
{
if (jo.Parent is JProperty)
{
var ParentName = ((JProperty)jo.Parent).Ancestors()
.Where(jt => jt.Type == JTokenType.Property)
.Select(jt => ((JProperty)jt).Name.ToString())
.FirstOrDefault();
if (ParentName == "Object")
{
parentKey = ((JProperty)jo.Parent).AncestorsAndSelf() // Climb up the json container parent/child hierachy
.Select(p => p.SelectToken("NM")) // Get the "parentKey" property in the current parent (if present)
.FirstOrDefault(k => k != null).ToString(); // Return the first one found.
}
if (jo is JObject)
{
// add a property for the parent
var joWork = new JObject(new JProperty("_Parent", parentKey));
// add only the string properties in the current object
joWork.Add(((JObject)jo).Properties()
.Where(p => p.Value.Type == JTokenType.String));
return new[] { joWork };
}
else if (jo is JArray)
{
groupId++;
return from o in jo
let arrayItems = ((JObject)o).Properties().Where(p => p.Value.Type == JTokenType.String).ToList()
where arrayItems.Count > 0
select new JObject(new[]
{
new JProperty("_Parent", parentKey),
new JProperty("_Group", groupId),
}
.Concat(arrayItems));
}
}
return Enumerable.Empty<JObject>();
}
)
.ToList();
Console.WriteLine(JsonConvert.SerializeObject(query, Formatting.Indented));
这里我在SelectMany()
方法中使用LINQ查询语法,因为它更紧凑。替代方法语法是:
else if (jo is JArray)
{
groupId++;
return jo.Select(o =>
{
var arrayItems = ((JObject)o).Properties()
.Where(p => p.Value.Type == JTokenType.String).ToList();
if (arrayItems.Count > 0)
{
var joWork = new JObject();
// add a property for the parent
joWork.Add(new JProperty("_Parent", parentKey));
joWork.Add(new JProperty("_Group", groupId));
// add only the string properties in the current object
joWork.Add(arrayItems);
return joWork;
}
else
{
return null;
}
})
.Where(o => o != null);
}