如何转换XDocument仅保留选定的路径?

时间:2018-10-19 17:12:58

标签: c# xml

我有很多(百万)xml文档,从小型到大型。我需要通过c#7处理(转换)它们,只留下一些路径(路径可能会有所不同;它们将由用户设置)。

一个文档的样本(目前我不在乎名称空间):

<root>
<a><aa1></aa1><aa2></aa2></a>
<b><bb></bb></b>
<c><cc></cc></c>
<d>d</d>
</root>

鉴于/root/a/aa1/root/d被列入白名单,我应该产生以下结果:

<root>
<a><aa1></aa1></a>
<d>d</d>
</root>

我想最好的方法是将白名单路径指定为XPath表达式的集合。

现有处理将xmls加载到XDocuments中。

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

或者,我可以删除所选元素的所有同级。如何执行该删除操作?

应考虑性能和内存占用。

2 个答案:

答案 0 :(得分:1)

尝试以下算法:

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

(b)从这组路径中生成XSLT样式表,其中(i)默认模板规则进行深度跳过(<xsl:template match="*"/>),(ii)每个给定路径的模板规则进行浅表副本(<xsl:copy><xsl:copy-of select="@*"/><xsl:apply-templates/></xsl:copy>)。

(c)在源文档上运行此生成的样式表。

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

答案 1 :(得分:0)

这将删除未列入白名单的节点,从而压缩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();
    }
}

注意::此代码远非快速/完美,但可以正常工作。