[英]group XDocument by multiple nodes (dynamic)
我有以下数据作为 DataTable 进来。 如果我直接将它转换为 XML,我会得到很好的 xdocument 对象。
但是问题是我需要按前 4 列对其进行分组,以便 XML 如下所示。 我知道数据表中只有三个节点“Segment”、“Price”和“Qty”其余列可能是动态的,不能使用硬编码名称(上述 3 除外)
<ROOT>
<ROW>
<Col1>CESLP</Col1>
<Col2>MRP</Col2>
<Col3>372</Col3>
<Date>20040101</Date>
<BID_INTERVALS>
<SEGMENT>1</SEGMENT>
<Price>10</Price>
<QTY>5</QTY>
</BID_INTERVALS>
<BID_INTERVALS>
<SEGMENT>2</SEGMENT>
<Price>15</Price>
<QTY>6</QTY>
</BID_INTERVALS>
</ROW>
<ROW>
<Col1>CESLP</Col1>
<Col2>MRP</Col2>
<Col3>372</Col3>
<Date>20040102</Date>
<BID_INTERVALS>
<SEGMENT>1</SEGMENT>
<Price>11</Price>
<QTY>5</QTY>
</BID_INTERVALS>
<BID_INTERVALS>
<SEGMENT>2</SEGMENT>
<Price>14.5</Price>
<QTY>6</QTY>
</BID_INTERVALS>
</ROW>
有什么解决办法吗? 我被困了很长一段时间,通过'except'尝试了xdocument group,但对我没有用。
编辑1:
我正在使用下面的代码对记录进行分组(使用此处的解决方案
dataTable.AsEnumerable()
.GroupBy(r => new NTuple<object>(from column in colNames select r[column]))
.Select(g => g.CopyToDataTable()).ToList();
从您的问题中的位图中,您最初是否拥有DataTable
或XDocument
并不完全清楚。 因此,假设您有一个XDocument
,并且您希望根据前四列的值对根元素的子行进行分组,其余值收集在名为<BID_INTERVALS>
的元素序列下。
这可以使用以下扩展方法来完成:
public static partial class XNodeExtensions
{
public static XElement CopyAndGroupChildrenByColumns(this XElement root, Func<XName, int, bool> columnFilter, XName groupName) =>
new XElement(root.Name,
root.Attributes(),
root.Elements()
.Select((row) => (row, key : row.Elements().Where((e, i) => columnFilter(e.Name, i)).Select(e => (e.Name, e.Value)).ToHashSet()))
.GroupByKeyAndSet(pair => pair.row.Name, pair => pair.key)
.Select(g => new XElement(g.Key.Key,
g.Key.Set.Select(p => new XElement(p.Name, p.Value)).Concat(g.Select(i => new XElement(groupName, i.row.Elements().Where((e, i) => !columnFilter(e.Name, i))))))));
public static IEnumerable<IGrouping<(TKey Key, HashSet<TItem> Set), TSource>> GroupByKeyAndSet<TSource, TKey, TItem>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, HashSet<TItem>> setSelector) =>
Enumerable.GroupBy(source, (i) => (keySelector(i), setSelector(i)), new CombinedComparer<TKey, HashSet<TItem>>(null, HashSet<TItem>.CreateSetComparer()));
}
public class CombinedComparer<T1, T2> : IEqualityComparer<ValueTuple<T1, T2>>
{
readonly IEqualityComparer<T1> comparer1;
readonly IEqualityComparer<T2> comparer2;
public CombinedComparer(IEqualityComparer<T1> comparer1, IEqualityComparer<T2> comparer2) => (this.comparer1, this.comparer2) = (comparer1 ?? EqualityComparer<T1>.Default, comparer2 ?? EqualityComparer<T2>.Default);
public bool Equals(ValueTuple<T1, T2> x, ValueTuple<T1, T2> y) => comparer1.Equals(x.Item1, y.Item1) && comparer2.Equals(x.Item2, y.Item2);
public int GetHashCode(ValueTuple<T1, T2> obj) => HashCode.Combine(comparer1.GetHashCode(obj.Item1), comparer2.GetHashCode(obj.Item2));
}
然后,给定一些XDocument doc
,您可以执行以下操作:
// Group by the first four columns with all remaining elements collected under a <BID_INTERVALS> sequence of elements:
XName groupName = doc.Root.Name.Namespace + "BID_INTERVALS";
var grouped = doc.Root.CopyAndGroupChildrenByColumns((n, i) => (i < 4), groupName);
var newDoc = new XDocument(grouped);
另一方面,如果您有一个DataTable dt
而不是XDocument
,则可以使用以下扩展方法直接将表转换为XDocument
:
public static partial class XNodeExtensions
{
public static XDocument ToXDocument(this DataTable dt, XmlWriteMode mode = XmlWriteMode.IgnoreSchema)
{
var doc = new XDocument();
using (var writer = doc.CreateWriter())
dt.WriteXml(writer, mode);
return doc;
}
}
然后做:
var doc = dt.ToXDocument(XmlWriteMode.IgnoreSchema);
演示小提琴在这里。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.