扁平化 xml 节点的 Linq 方法语法
Posted
技术标签:
【中文标题】扁平化 xml 节点的 Linq 方法语法【英文标题】:Linq method syntax to flatten xml nodes 【发布时间】:2019-10-15 15:38:57 【问题描述】:我有下面的工作代码来展平 xml 节点:
var xml = XDocument.Parse(input);
var headerSection = xml.Descendants("Header");
var subHeaderSection = xml.Descendants("SubHeader");
var body = xml.Descendants("Body");
var activity = body.Descendants("Activity");
var position = body.Descendants("Position");
var positionAmouts = position.Descendants("PositionAmounts").Elements("PositionAmount");
var results = from h in headerSection
from sh in subHeaderSection
from a in activity
from po in position
from pa in positionAmouts
select new List<string>
h.Element("Id").Value,
h.Element("OtherId").Value,
h.Element("SomeValue").Value,
sh.Element("SomeValue2").Value,
sh.Element("SomeValue3").Value,
a.Element("ActivityId").Value,
a.Element("ActivityValue").Value,
po.Element("PositionId").Value,
pa.Element("Amount1").Value,
pa.Element("Amount2").Value,
此代码运行良好。但实际的 froms 列表比显示的要长。列表发起者变成了一个长长的值列表。
我相信如果我可以将许多 froms 转换为 linq 方法语法,我应该能够在一种方法中生成相同的 List 结果,在该方法中我可以做的事情比列出所有所需的值更好? (就像循环通过一些预定义的元素名称)
编辑:根据要求的示例 xml:
<Root>
<Header>
<Id>123</Id>
<OtherId>456</OtherId>
<SomeValue>abc</SomeValue>
</Header>
<SubHeader>
<SomeValue2>dfg</SomeValue2>
<SomeValue3>fghj</SomeValue3>
</SubHeader>
<Body>
<Activity>
<ActivityId>321</ActivityId>
<ActivityValue>hjk</ActivityValue>
</Activity>
<Position>
<PositionId>654</PositionId>
<PositionAmounts>
<PositionAmount>
<Amount1>10.01</Amount1>
<Amount2>12.63</Amount2>
</PositionAmount>
<PositionAmount>
<Amount1>15.11</Amount1>
</PositionAmount>
</PositionAmounts>
</Position>
</Body>
</Root>
【问题讨论】:
能否提供原xml? @vladimir,已添加 【参考方案1】:试试这个方法:
const string splitterElementName = "PositionAmount";
var doc = XDocument.Parse(input);
var result = doc.Descendants()
// Take just the deepest descendants.
.Where(v => !v.HasElements)
.Select(v => v.AncestorsAndSelf())
// Group by key, where key contains names of all ancestors exclude the deepest element.
.GroupBy(k =>
var isSplitter = false;
var key = string.Join("-", k
.Skip(1)
.Select(v =>
if (v.Name.LocalName != splitterElementName)
return v.Name.LocalName;
isSplitter = true;
// Define the unique id for each splitting element.
return $"v.Name.LocalName:string.Join(":", v.AncestorsAndSelf().Select(a => a.ElementsBeforeSelf().Count()))";
));
return (key, isSplitter);
,
e => e.First().Value)
// Merge all groups to one list.
.GroupBy(k => 0, e => e, (key, element) => element.ToArray())
// Extract all splitters.
.SelectMany(groups => groups
.Where(group => group.Key.isSplitter)
.Select(splittingGroup => (splittingKey: splittingGroup.Key, groups)))
// Prepare result.
.Select(v => v.groups
.Where(group => !group.Key.isSplitter || group.Key == v.splittingKey)
.SelectMany(a => a)
.ToArray())
.ToList();
/* result:
[0]: string[10]
[1]: string[9]
result[0]
string[10]
[0]: "123"
[1]: "456"
[2]: "abc"
[3]: "dfg"
[4]: "fghj"
[5]: "321"
[6]: "hjk"
[7]: "654"
[8]: "10.01"
[9]: "12.63"
result[1]
string[9]
[0]: "123"
[1]: "456"
[2]: "abc"
[3]: "dfg"
[4]: "fghj"
[5]: "321"
[6]: "hjk"
[7]: "654"
[8]: "15.11"
*/
测试xml:
var input = @"<Root>
<Header>
<Id>123</Id>
<OtherId>456</OtherId>
<SomeValue>abc</SomeValue>
</Header>
<SubHeader>
<SomeValue2>dfg</SomeValue2>
<SomeValue3>fghj</SomeValue3>
</SubHeader>
<Body>
<Activity>
<ActivityId>321</ActivityId>
<ActivityValue>hjk</ActivityValue>
</Activity>
<Position>
<PositionId>654</PositionId>
<PositionAmounts>
<PositionAmount>
<Amount1>10.01</Amount1>
<Amount2>12.63</Amount2>
</PositionAmount>
<PositionAmount>
<Amount1>15.11</Amount1>
</PositionAmount>
</PositionAmounts>
</Position>
</Body>
</Root>";
【讨论】:
问题中的原始代码生成了一个 List>,所有部分都被折叠 答案已修复,目前的方式看起来有点难看;)以上是关于扁平化 xml 节点的 Linq 方法语法的主要内容,如果未能解决你的问题,请参考以下文章
LeetCode 430 扁平化多级双向链表[递归 栈] HERODING的LeetCode之路
LeetCode - 430 - 扁平化多级双向链表 - Java - 细喔