【问题标题】:Change attributes in nested XML更改嵌套 XML 中的属性
【发布时间】:2020-01-31 16:45:05
【问题描述】:

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

【问题讨论】:

  • 你没有任何数字元素;它们都是字符串。所以 element = element + 1 不适用于字符串。您应该隔离数字,增加它,然后重建字符串。
  • 例如,您将如何增加id="step23c3"

标签: xml vb.net


【解决方案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>

【讨论】:

  • 感谢您的代码。你的例子是我需要的,除了'internalRef'没有改变refID(它仍然说“step23c1”)。您的代码看起来像是在 C 中,是否有可能在 VB.Net 中恢复代码?我不熟悉 C 并且迷失在其中。谢谢你,珍
  • 我尝试将您的代码放入 C# 文件,但收到以下错误“有多个根元素。第 4 行,位置 2”因为我不知道 C,所以我不知所措去做。 @jdweng​​span>
  • 我将代码更改为 VB.Net 并添加了行以保存输出。
  • 谢谢@jdweng​​span>
  • internalRefID 有作为 idRefef 被引用到 goto 的步骤,但它应该是最初被引用的 ID。因此 step3.3.2 的 internalRefId 为 step3.3.1,并且该 refID 指向 step3.3.1。我知道这不是最好的解释。但试着简单地说,每个 internalRefId 都有它引用的程序步骤 id。运行代码后,这些 internalRefId 现在显示它引用的程序步骤 ID,而不是它要链接到的 internalRefId。
【解决方案2】:

这个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

不过,我们仍然缺少增量逻辑,因此您需要想出它,我们可以再次提供帮助。

【讨论】:

  • XmlSerializer 真的让我很困惑。谢谢你的回复,我看看能不能让它工作。
  • @JenPann 熟悉Serialization。它只是在 .NET 类和文件或流之间移动数据的一种方式,而 Xml 序列化是它的子集。 XmlSerialization 内置于 .NET Framework 中,它使处理 Xml 文件变得更加容易。
  • 我将阅读序列化。谢谢你的建议@djv
【解决方案3】:

所以这是一个猜测,因为没有明确定义的“增量”。用于测试的文字。

    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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-11-03
    • 1970-01-01
    • 1970-01-01
    • 2021-03-23
    • 1970-01-01
    • 2013-09-22
    • 2018-09-06
    • 2013-08-16
    相关资源
    最近更新 更多