更改嵌套XML中的属性

时间:2020-01-31 16:45:05

标签: xml vb.net

我有一个带有嵌套元素/节点的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

3 个答案:

答案 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?根据我的收集,您需要增加这些值

  • “ step21”
  • “ step22”
  • “ step23”
  • “ step23a”
  • “ step23b”
  • “ step23c”
  • “ step23c1”
  • “ step23c2”
  • “ step23c3”

您需要定义它。编写一个接受字符串并根据您的规则递增的函数。如果您提供增加的值,我们可能会提供逻辑帮助。因此,添加新的增量功能,

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