我有很多(百万)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?
或者,我可以删除所选元素的所有同级。如何执行该删除操作?
应考虑性能和内存占用。
答案 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();
}
}
注意::此代码远非快速/完美,但可以正常工作。