如何从 VB.NET 中的 XML 文件构建唯一的树结构
Posted
技术标签:
【中文标题】如何从 VB.NET 中的 XML 文件构建唯一的树结构【英文标题】:How can I build a unique tree structure from an XML file in VB.NET 【发布时间】:2021-12-25 03:13:33 【问题描述】:我需要使用 XML 文件中的计数创建一个唯一的节点和属性列表。实际上,我需要对结构中等未知的文件中的每个节点/属性组合进行计数。
例如,如果我有一个看起来像这样的 XML 文件;
<level1>
<item id="a">blah</item>
<item id="b">blah</item>
<item id="c">blah</item>
<level2>
<item id="d">blah</item>
<item id="e">blah</item>
<item id="f">blah</item>
<level3>
<item id="g">blah</item>
<item id="h">blah</item>
<item id="i">blah</item>
<item id="k">blah</item>
</level3>
<level3>
<item id="g">blah</item>
<item id="h">blah</item>
<item id="i">blah</item>
<item id="j">blah</item>
</level3>
</level2>
<level2>
<item id="d">blah</item>
<item id="e">blah</item>
<item id="f">blah</item>
<item id="k">blah</item>
<level3>
<item id="g">blah</item>
<item id="i">blah</item>
</level3>
<level3>
<item id="g">blah</item>
<item id="h">blah</item>
<item id="j">blah</item>
</level3>
</level2>
<level1>
我可能想要一些看起来像的东西;
<level1 count="1">
<item id="a">1</item>
<item id="b">1</item>
<item id="c">1</item>
<level2 count="2">
<item id="d">2</item>
<item id="e">2</item>
<item id="f">2</item>
<item id="k">1</item>
<level3 count="4">
<item id="g">4</item>
<item id="h">3</item>
<item id="i">3</item>
<item id="j">2</item>
<item id="k">1</item>
</level3>
</level2>
<level1>
虽然我已将输出表示为任意的 XML 结构。真的只需要一份信息报告。
我的开发环境是使用VS2019的VB.NET。我有读取 XML 树的代码,但我需要帮助设计存储数据的结构,以及搜索它以增加计数器的能力。
For Each Element As XmlElement In XmlDoc.SelectNodes("//*")
Console.WriteLine("Processing element with name 0:", Element.Name)
For Each Attribute As XmlAttribute In Element.Attributes
Console.WriteLine("0: 1", Attribute.Name, Attribute.Value)
Next
Console.WriteLine()
Next
【问题讨论】:
您可以在 XQuery、XSLT(例如 Saxon 10 支持 .NET 框架)或使用 LINQ to XML 中轻松递归地对元素进行分组和计数。使用 DOM 似乎对这项任务没有帮助。 您是否知道任何可以链接到的与我感兴趣的事情类似的示例? 好吧,之前已经在这里使用了分组和递归,但是从那个单个示例中您的要求并不清楚。你想数数吗?所有嵌套级别的所有<item id="a">blah</item>
?还是仅当它们处于相同深度时?
【参考方案1】:
您可以使用 Saxon 10 HE(或 9.9 或 9.8)在 .NET 框架中运行的示例 XSLT 3 样式表是
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="#all"
version="3.0">
<xsl:function name="mf:group" as="node()*">
<xsl:param name="parents" as="node()*"/>
<xsl:for-each-group select="$parents/*" composite="yes" group-by="node-name(), @id">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:attribute name="count" select="count(current-group())"/>
<xsl:sequence select="mf:group(current-group())"/>
</xsl:copy>
</xsl:for-each-group>
</xsl:function>
<xsl:template match="/">
<xsl:sequence select="mf:group(.)"/>
</xsl:template>
<xsl:output method="xml" indent="yes" />
</xsl:stylesheet>
在线示例位于https://xsltfiddle.liberty-development.net/bESWQsP,输入的输出(关闭根元素的结束标记后)例如
<level1 count="1">
<item id="a" count="1"/>
<item id="b" count="1"/>
<item id="c" count="1"/>
<level2 count="2">
<item id="d" count="2"/>
<item id="e" count="2"/>
<item id="f" count="2"/>
<level3 count="4">
<item id="g" count="4"/>
<item id="h" count="3"/>
<item id="i" count="3"/>
<item id="k" count="1"/>
<item id="j" count="2"/>
</level3>
<item id="k" count="1"/>
</level2>
</level1>
所以我认为它有你正在寻找的计数。
用于 Saxon 10 的 .NET API 记录在 https://saxonica.com/html/documentation10/dotnetdoc/index.html 并在 https://saxonica.com/documentation10/index.html#!dotnet/dotnetapi 进行了介绍,因此您基本上使用 Processor
对象开始,创建一个 XsltCompiler
将上述 XSLT 文档编译为 @987654331 @ 然后从中创建一个 Xslt30Transformer
以向它提供您首先使用从 Processor
创建的 DocumentBuilder
构建的 XML 输入到 ApplyTemplates
方法或 Transform
方法而不使用 DocumentBuilder
,只需FileStream
。
因此,没有任何错误处理(例如,在编译或 XSLT 执行会出错的情况下)的最短示例是
Imports System.IO
Imports Saxon.Api
Module Module1
Sub Main()
Dim processor = New Processor()
Dim xsltCompiler = processor.NewXsltCompiler()
Dim xsltExecutable As XsltExecutable
Using fs = File.OpenRead("recursive-grouping.xsl")
xsltExecutable = xsltCompiler.Compile(fs)
End Using
Dim xslt30Processor = xsltExecutable.Load30()
Using fs = File.OpenRead("input-sample.xml")
xslt30Processor.Transform(fs, processor.NewSerializer(Console.Out))
End Using
End Sub
End Module
在线https://github.com/martin-honnen/SaxonXSLT30ExampleWithVB。
或者使用 LINQ 和 LINQ to XML 进行分组,至少只要你只需要通过已知的 id 属性进行分组:
Sub Main()
Dim doc = <?xml version="1.0"?>
<level1>
<item id="a">blah</item>
<item id="b">blah</item>
<item id="c">blah</item>
<level2>
<item id="d">blah</item>
<item id="e">blah</item>
<item id="f">blah</item>
<level3>
<item id="g">blah</item>
<item id="h">blah</item>
<item id="i">blah</item>
<item id="k">blah</item>
</level3>
<level3>
<item id="g">blah</item>
<item id="h">blah</item>
<item id="i">blah</item>
<item id="j">blah</item>
</level3>
</level2>
<level2>
<item id="d">blah</item>
<item id="e">blah</item>
<item id="f">blah</item>
<item id="k">blah</item>
<level3>
<item id="g">blah</item>
<item id="i">blah</item>
</level3>
<level3>
<item id="g">blah</item>
<item id="h">blah</item>
<item id="j">blah</item>
</level3>
</level2>
</level1>
Console.WriteLine(New XDocument(GroupChildren(doc)))
End Sub
Function GroupChildren(parents As IEnumerable(Of XContainer)) As IEnumerable(Of XContainer)
Return From child In parents.Elements()
Group By nameCount = New With
Key .Name = child.Name,
Key .Id = child.@id
Into group = Group
Select <<%= nameCount.Name %> Count=<%= group.Count() %> Id=<%= nameCount.Id %>>
<%= GroupChildren(group) %>
</>
End Function
【讨论】:
我喜欢 Saxon 10 样式表方法的概念。我已经开始看文档了。为了快速开始我的理解,在 VB.NET、C# 中是否有任何代码示例? 下载页面 saxonica.com/download/download_page.xml#resources 有一个资源 zip,其中包含 C# 示例(连同 Java 和 XML,请查看cs
文件夹)。其中一些也在线saxonica.plan.io/projects/saxon/repository/he/revisions/master/…。恐怕我不知道任何 VB.NET 示例,但 C# 和 VB.NET 之间的大多数“转录”相对容易,甚至可能有在线转换器。以上是关于如何从 VB.NET 中的 XML 文件构建唯一的树结构的主要内容,如果未能解决你的问题,请参考以下文章
VB.NET:使用 XDocument 在 XML 文件中添加/编辑/删除 XElement