用于展平 xml 个节点的 Linq 方法语法
Linq method syntax to flatten xml nodes
我有下面的工作代码来展平 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 转换为 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>
试试这个方法:
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>";
我有下面的工作代码来展平 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 转换为 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>
试试这个方法:
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>";