C# 如何转换XDocument,只留下选定的路径?

C# 如何转换XDocument,只留下选定的路径?,c#,xml,C#,Xml,我有数百万个从小到大的xml文档。我有一个要求,通过c7处理转换它们,只留下一些路径可能会有所不同;它们将由用户设置 我目前不关心名称空间的一个文档示例: <root> <a><aa1></aa1><aa2></aa2></a> <b><bb></bb></b> <c><cc></cc></c> <d>d&

我有数百万个从小到大的xml文档。我有一个要求,通过c7处理转换它们,只留下一些路径可能会有所不同;它们将由用户设置

我目前不关心名称空间的一个文档示例:

<root>
<a><aa1></aa1><aa2></aa2></a>
<b><bb></bb></b>
<c><cc></cc></c>
<d>d</d>
</root>
我想最好的方法是将白名单路径指定为XPath表达式的集合

现有处理将XML加载到XDocuments中

我可以通过XPathSelectElements选择必要的元素。问题是:如何将它们复制到新的XDocument

或者,我可以删除选定图元的所有同级。如何执行该删除


应考虑性能和内存占用。

尝试以下算法:

a扩展给定的路径集以包括这些路径的所有前缀,因此从/root/a/aa1、/root/d可以得到/root、/root/a、/root/a/aa1、/root/d

b从这组路径生成XSLT样式表,其中i默认模板规则执行深度跳过,ii每个给定路径的模板规则执行浅层复制

c在源文档上运行生成的样式表


您可能会发现,在XPath 3.1/XSLT 3.0中,您可以使用EQName表示法处理名称空间敏感路径,例如match=Q{some uri}root/Q{some uri}a。对于早期的XPath版本,处理引用命名空间元素名称的路径总是一个问题。这同样适用于非XSLT解决方案。

这将删除非白名单节点,从而压缩XML文档:

/// <summary> Siblings including self </summary>
public static IEnumerable<XElement> Siblings(this XElement xml) =>
    xml?.Parent?.Elements() ?? new List<XElement>();

/// <summary> Ancestors, descendants and self </summary>
public static IEnumerable<XElement> AncestorsDescendantsSelf(this XElement xml) =>
    xml?.DescendantsAndSelf()?.Union(xml?.Ancestors() ?? new List<XElement>()) ?? new List<XElement>();

/// <summary> Compress the document by removing everything except the elemnents along selected paths </summary>
/// <param name="xml">source document to be modified</param>
/// <param name="whitelistedPaths">collection of xpath paths</param>
public static void Compress(this XDocument xml, IEnumerable<string> whitelistedPaths) {
    var siblings = nodes.SelectMany(n => n.AncestorsAndSelf()).Aggregate((new List<XElement>()).AsEnumerable(), (n1,n2) => n1.Union(n2.Siblings()));
    var lineages = nodes.SelectMany(n => n.AncestorsDescendantsSelf());
    var nodesToDelete = siblings.Except(lineages).ToList();
    foreach (var element in nodesToDelete) {
        element.Remove();
    }
}

注意:这段代码远不是很快/完美,但它可以正常工作。

看看XSLT,应该有很多关于如何通过C实现的示例。@MichaelDaniloff XSLT将是我最后的选择,因为它的身份转换正在成为pia。如果你关心namespacesthx,这是个好主意。我已经尝试过它的一种变体,但失败了。我已经尝试使用祖先轴和后代轴动态扩展模板匹配属性中的路径。XMLSpy说这是一个错误的语法
/// <summary> Siblings including self </summary>
public static IEnumerable<XElement> Siblings(this XElement xml) =>
    xml?.Parent?.Elements() ?? new List<XElement>();

/// <summary> Ancestors, descendants and self </summary>
public static IEnumerable<XElement> AncestorsDescendantsSelf(this XElement xml) =>
    xml?.DescendantsAndSelf()?.Union(xml?.Ancestors() ?? new List<XElement>()) ?? new List<XElement>();

/// <summary> Compress the document by removing everything except the elemnents along selected paths </summary>
/// <param name="xml">source document to be modified</param>
/// <param name="whitelistedPaths">collection of xpath paths</param>
public static void Compress(this XDocument xml, IEnumerable<string> whitelistedPaths) {
    var siblings = nodes.SelectMany(n => n.AncestorsAndSelf()).Aggregate((new List<XElement>()).AsEnumerable(), (n1,n2) => n1.Union(n2.Siblings()));
    var lineages = nodes.SelectMany(n => n.AncestorsDescendantsSelf());
    var nodesToDelete = siblings.Except(lineages).ToList();
    foreach (var element in nodesToDelete) {
        element.Remove();
    }
}