I have an XML file with nested elements/nodes. I need to increment the <proceduralStep>
attribute "id" for each node and its child nodes. My first problem is I haven't been able to make changes to the attribute using node.Attributes("id").Value = node.Attributes("id").Value + 1
. It gives an error on the node.Attributes("id").Value +1
. This is the parent element /proceduralStep/
. Second problem is I need each nodes attribute to be changed if it's a child element of <proceduralStep>
. So if it's /proceduralStep/proceduralStep
that attributes id will be changed to 1.1. I've been searching the net for examples and explanations on how to do this but haven't found any that work.
Sample 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>
Not working code
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
Real simple using a recursive algorithm and 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
Output
<?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>
thank you for the code. Your example is spot on with what I need except for the 'internalRef' not having the refID changed (it still saids "step23c1"). Your code looks like it's in C would it be possible to get the code back in VB.Net? I'm not familiar with C and get lost in it. Thank you Jen
I tried putting your code into a C# file but received the following error "There are multiple root elements. Line 4, position 2" Since I don't know C I'm at a loss for what to do. @jdweng
I changed code to VB.Net and added line to save output.
Thank you @jdweng
the internalRefID has the step it was referenced in as the idRefef to goto, but it should be the ID that was referencenced originally. So step3.3.2 has an internalRefId of step3.3.1 and that refID gets pointed to step3.3.1. I know this is not the best explanation. But try to put it simply, each internalRefId has the procedural step id it references. After running the code those internalRefId's now show the procedural step id it is referencing from instead of the internalRefId it's mean to link to.