我有一个带有嵌套元素/节点的XML文件。我需要为每个节点及其子节点增加<proceduralStep>
属性“ id”。我的第一个问题是我无法使用node.Attributes("id").Value = node.Attributes("id").Value + 1
来更改属性。它在node.Attributes("id").Value +1
上给出错误。这是父元素/proceduralStep/
。第二个问题是,如果它是<proceduralStep>
的子元素,则需要更改每个节点属性。因此,如果是/proceduralStep/proceduralStep
,则属性id将更改为1.1。我一直在网上搜索有关如何执行此操作的示例和说明,但没有找到任何有效的方法。
示例XML
<dmodule>
<mainProcedure>
<proceduralStep id="step1">
<para>Step 1</para>
</proceduralStep>
<proceduralStep id="step2">
<figure id="fig2">
<title>xxxxx</title>
<graphic infoEntityIdent="ICN-GAASI"></graphic>
</figure>
</proceduralStep>
<proceduralStep id="step3">
<para>Step 3 with link to step 2 (ID 23) here:
<internalRef internalRefId="step2" internalRefTargetType="step"></internalRef></para>
<figure id="fig3">
<title>xxxxx</title>
<graphic infoEntityIdent="ICN-GAASIB0"></graphic>
</figure>
<proceduralStep id="step3.1">
<para>Step 3.2 with link to step 3.1 (ID 23a) here:
<internalRef internalRefId="step3.1" internalRefTargetType="step"></internalRef></para>
</proceduralStep>
<proceduralStep id="step3.2">
<figure>
<title>xxxxx</title>
<graphic infoEntityIdent="ICN-GAASIB0-00-"></graphic>
</figure>
<proceduralStep id="step3.2.1">
<figure>Step 3.3.1</figure>
</proceduralStep>
<proceduralStep id="step3.2.2">
<para>Step 3.3.2 with link to step 3.3.1 (ID 23c1) here:
<internalRef internalRefId="step3.2.1" internalRefTargetType="step"></internalRef></para>
</proceduralStep>
<proceduralStep id="step3.2.3">
<figure>Step 3.3.3</figure>
</proceduralStep>
</proceduralStep>
</proceduralStep>
</mainProcedure>
</dmodule>
无效代码
Dim doc As XDocument = XDocument.Load(FILENAME)
Dim directoryName As String = Path.GetDirectoryName(FILENAME)
Dim root As XElement = doc.Root
Dim prefixStep As String = "step"
Dim prefixFig As String = "fig"
Dim nameResult As String = Path.GetFileName(FILENAME)
Dim ns As XNamespace = root.GetDefaultNamespace()
Dim mainProcedure As XElement = root.Descendants("mainProcedure").FirstOrDefault()
RenumberStep(mainProcedure, prefixStep, ns)
RenumberFigures(mainProcedure, prefixFig, ns)
For Each internalRef As XElement In doc.Descendants(ns + "internalRef")
Dim oldId As String = CType(internalRef.Attribute("internalRefId"), String)
If Not oldId Is Nothing Then
If dictionary.ContainsKey(oldId) Then
internalRef.SetAttributeValue("internalRefId", dictionary(oldId))
Else
' internalRef.SetAttributeValue("internalRefId", "Error : " & oldId)
End If
End If
Next internalRef
doc.Save(FILENAME)
Module Module1
Public dictionary As New Dictionary(Of String, String)
Public dictionaryFig As New Dictionary(Of String, String)
Sub RenumberStep(parent As XElement, prefix As String, ns As XNamespace)
Dim index As Integer = 1
For Each proceduralStep As XElement In parent.Elements(ns + "proceduralStep")
Dim oldId = CType(proceduralStep.Attribute("id"), String)
If Not oldId Is Nothing Then
dictionary.Add(oldId, prefix + index.ToString())
proceduralStep.SetAttributeValue("id", prefix + index.ToString())
RenumberStep(proceduralStep, prefix + index.ToString() + ".", ns)
Else
proceduralStep.SetAttributeValue("id", prefix + index.ToString())
End If
index = index + 1
Next proceduralStep
End Sub
Sub RenumberFigures(parent As XElement, prefix As String, ns As XNamespace)
Dim index As Integer = 1
For Each figure As XElement In parent.Elements(ns + "figure")
Dim oldfigId = CType(figure.Attribute("id"), String)
If Not oldfigId Is Nothing Then
dictionaryFig.Add(oldfigId, prefix + index.ToString())
figure.SetAttributeValue("id", prefix + index.ToString())
RenumberFigures(figure, prefix + index.ToString() + ".", ns)
Else
figure.SetAttributeValue("id", prefix + index.ToString())
End If
index = index + 1
Next figure
End Sub
End Module
答案 0 :(得分:1)
使用递归算法和xml linq真正简单:
Module Module1
Const FILENAME As String = "c:\temp\test.xml"
Const OUTPUT_FILENAME As String = "c:\temp\test1.xml"
Public dictionary As New Dictionary(Of String, String)
Sub Main()
Dim doc As XDocument = XDocument.Load(FILENAME)
Dim root As XElement = doc.Root
Dim ns As XNamespace = root.GetDefaultNamespace()
Dim mainProcedure As XElement = root.Descendants("mainProcedure").FirstOrDefault()
Dim prefix As String = "step"
Renumber(mainProcedure, prefix, ns)
For Each internalRef As XElement In doc.Descendants(ns + "acronymTerm")
Dim oldId As String = CType(internalRef.Attribute("internalRefId"), String)
If Not oldId Is Nothing Then
If dictionary.ContainsKey(oldId) Then
internalRef.SetAttributeValue("internalRefId", dictionary(oldId))
Else
internalRef.SetAttributeValue("internalRefId", "Error : " & oldId)
End If
End If
Next internalRef
doc.Save(OUTPUT_FILENAME)
End Sub
Sub Renumber(parent As XElement, prefix As String, ns As XNamespace)
Dim index As Integer = 1
For Each proceduralStep As XElement In parent.Elements(ns + "proceduralStep")
Dim oldId = CType(proceduralStep.Attribute("id"), String)
dictionary.Add(oldId, prefix + index.ToString())
proceduralStep.SetAttributeValue("id", prefix + index.ToString())
Renumber(proceduralStep, prefix + index.ToString() + ".", ns)
index = index + 1
Next proceduralStep
End Sub
End Module
输出
<?xml version="1.0" encoding="utf-8"?>
<dmodule xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.s1000d.org/S1000D_4-0-1/xml_schema_flat/proced.xsd">
<mainProcedure>
<proceduralStep id="step1">
<para>XX xxx<acronym id="mosim">XX xxx<acronymTerm>XX xxx</acronymTerm>XX xxx<acronymDefinition>XX xxx</acronymDefinition>XX xxx</acronym>XX xxx<internalRef internalRefId="Error : Error : fig1" internalRefTargetType="figure" targetTitle="fig1">XX xxx</internalRef>XX xxx</para>
<proceduralStep id="step1.1">
<para>XX xxx</para>
</proceduralStep>
<proceduralStep id="step1.2">
<para>XX xxx<acronymTerm internalRefId="mosim">XX xxx</acronymTerm>XX xxx</para>
</proceduralStep>
<proceduralStep id="step1.3">
<para>XX xxx</para>
</proceduralStep>
</proceduralStep>
<proceduralStep id="step2">
<para>XX xxx<acronymTerm internalRefId="mosim">XX xxx</acronymTerm>XX xxx<internalRef internalRefId="Error : Error : fig1" internalRefTargetType="figure" targetTitle="fig1">XX xxx</internalRef>XX xxx</para>
<proceduralStep id="step2.1">
<para>XX xxx<acronymTerm internalRefId="mosim">XX xxx</acronymTerm>XX xxx</para>
</proceduralStep>
<proceduralStep id="step2.2">
<para>XX xxx<acronymTerm internalRefId="mosim">XX xxx</acronymTerm>XX xxx</para>
</proceduralStep>
</proceduralStep>
<proceduralStep id="step3">
<para>XX xxx<emphasis>XX xxx</emphasis>XX xxx</para>
</proceduralStep>
<proceduralStep id="step4">
<para>XX xxx<emphasis>XX xxx</emphasis>XX xxx</para>
</proceduralStep>
<proceduralStep id="step5">
<para>XX xxx<acronymTerm internalRefId="lola">XX xxx</acronymTerm>XX xxx<acronym id="cd">XX xxx<acronymTerm>XX xxx</acronymTerm>XX xxx<acronymDefinition>XX xxx</acronymDefinition>XX xxx</acronym>XX xxx<acronymTerm internalRefId="mosim">XX xxx</acronymTerm>XX xxx<acronymTerm internalRefId="cd">XX xxx</acronymTerm>XX xxx<acronym id="dvd">XX xxx<acronymTerm>XX xxx</acronymTerm>XX xxx<acronymDefinition>XX xxx</acronymDefinition>XX xxx</acronym>XX xxx</para>
</proceduralStep>
</mainProcedure>
</dmodule>
答案 1 :(得分:0)
此node.Attributes("id").Value + 1
不起作用,因为您不能将数字1添加到字符串中。那么,如何增加您的ID?根据我的收集,您需要增加这些值
您需要定义它。编写一个接受字符串并根据您的规则递增的函数。如果您提供增加的值,我们可能会提供逻辑帮助。因此,添加新的增量功能,
Public Function IncrementId(id As String) As String
Return id & "incremented"
End Function
并更改代码以调用新的增量函数,
node("proceduralStep").SetAttribute("id", IncrementId(node.Attributes("id").Value))
我想应该这样做。
但是,出于某些原因,我更喜欢Xml序列化而不是XmlDocument。最大的原因是,您可以使用.NET类对数据进行建模,并且获得了强大的输入!例如,System.Xml.XmlAttribute.Value始终是字符串,但有时Xml文件中的数据不是。对于您而言,它恰好是这样。
这就是我要做的。添加这些类来定义您的数据模型
<XmlRoot>
Public Class dmodule
<XmlElement("proceduralStep")>
Public Property proceduralSteps As List(Of proceduralStep)
End Class
Partial Public Class proceduralStep
<XmlElement("proceduralStep")>
Public Property proceduralSteps As List(Of proceduralStep)
<XmlAttribute>
Public Property id As String
<XmlElement>
Public Property para As para
End Class
Public Class para
<XmlText>
Public Property Description As String
<XmlElement>
Public Property internalRef As internalRef
End Class
Public Class internalRef
<XmlAttribute>
Public Property internalRefId As String
<XmlAttribute>
Public Property internalRefTargetType As String
End Class
有了这些,您可以将xml反序列化为内存中可以强权声明的.NET对象,可以对其进行迭代和修改(而不是将字符串传递到doc.SelectNodes("/dmodule/proceduralStep")
中)
现在您可以反序列化(文件>>内存)并序列化(内存>>文件)。
Dim myDmodule As dmodule
Dim serializer As New XmlSerializer(GetType(dmodule))
' read to memory
Using sr As New StreamReader("C:\Test\34 XML Parsing\XML File\CascadingStepsExample.xml")
myDmodule = CType(serializer.Deserialize(sr), dmodule)
End Using
' write to file
Using sw As New StreamWriter("C:\Test\34 XML Parsing\XML File\CascadingStepsExample_inc.xml")
serializer.Serialize(sw, myDmodule)
End Using
您可以添加一些函数以递归方式找到所有id属性并对其进行递增
' increment function
Public Shared Function incrementId(id As String) As String
Return id & "incremented" ' how do you REALLY increment this?
End Function
' recursive id finder and incrementer method
Public Shared Sub incrementIds(steps As IEnumerable(Of proceduralStep))
For Each s In steps
If Not String.IsNullOrEmpty(s.id) Then
s.id = incrementId(s.id)
End If
incrementIds(s.proceduralSteps)
Next
End Sub
反序列化到模型并序列化回文件后,只需调用该函数即可。
Dim myDmodule As dmodule
Dim serializer As New XmlSerializer(GetType(dmodule))
Using sr As New StreamReader("C:\Test\34 XML Parsing\XML File\CascadingStepsExample.xml")
myDmodule = CType(serializer.Deserialize(sr), dmodule)
End Using
' increment recursively
incrementIds(myDmodule.proceduralSteps)
Using sw As New StreamWriter("C:\Test\34 XML Parsing\XML File\CascadingStepsExample_inc.xml")
serializer.Serialize(sw, myDmodule)
End Using
仍然,我们缺少增量逻辑,因此您需要提出它,然后,我们可以提供帮助。
答案 2 :(得分:0)
这是一个猜测,因为没有明确定义的“增量”。用于测试的文字。
Dim strXMLPath As String = "C:\Test\34 XML Parsing\XML File\CascadingStepsExample.xml"
Dim doc As XElement
' doc = XElement.Load(strXMLPath)
'for texting
doc = <dmodule>
<proceduralStep id="step21">
<para>Step 1</para>
</proceduralStep>
<proceduralStep id="step22">
<para>Step 2</para>
</proceduralStep>
<proceduralStep id="step23">
<para>Step 3 with link to step 2 (ID 23) here:
<internalRef internalRefId="step23" internalRefTargetType="step"></internalRef>
</para>
<proceduralStep id="step23a">
<para>Step 3.1</para>
</proceduralStep>
<proceduralStep id="step23b">
<para>Step 3.2 with link to step 3.1 (ID 23a) here:
<internalRef internalRefId="step23a" internalRefTargetType="step"></internalRef>
</para>
</proceduralStep>
<proceduralStep id="step23c">
<para>Step 3.3</para>
<proceduralStep id="step23c1">
<para>Step 3.3.1</para>
</proceduralStep>
<proceduralStep id="step23c2">
<para>Step 3.3.2 with link to step 3.3.1 (ID 23c1) here:
<internalRef internalRefId="step23c1" internalRefTargetType="step"></internalRef>
</para>
</proceduralStep>
<proceduralStep id="step23c3">
<para>Step 3.3.3</para>
</proceduralStep>
<!-- end of step 3.3.3-->
</proceduralStep>
<!--end of step 3.3-->
</proceduralStep>
<!--end of step 3-->
</dmodule>
For Each el As XElement In doc...<proceduralStep>
'increment logic GUESS
Dim prfx As New System.Text.StringBuilder
Dim num As New System.Text.StringBuilder
Dim sufx As New System.Text.StringBuilder
Dim id As New System.Text.StringBuilder(el.@id)
Dim inNum As Boolean = False
Dim inSuf As Boolean = False
For x As Integer = 0 To id.Length - 1
Dim n As Boolean = False
If Integer.TryParse(id(x), Nothing) Then
n = True
End If
Select Case True
Case inSuf
sufx.Append(id(x))
Case inNum AndAlso n
num.Append(id(x))
Case inNum
inSuf = True
sufx.Append(id(x))
Case n
inNum = True
num.Append(id(x))
Case Else
prfx.Append(id(x))
End Select
Next
If num.Length > 0 Then
Dim i As Integer = Integer.Parse(num.ToString)
i += 1
el.@id = prfx.ToString & i.ToString & sufx.ToString
End If
Next