Search code examples
xmlvbscriptmsxml

Add child entry to a specific node in xml file in vbscipt


I have an xml file DataConfiguration.xml with this entry

<DataSource>
 <localdata>
    <add context="Localization">
       <parameter name="timeout" type="int" defaultvalue="60"/>
       <parameter name="address" type="string" defaultvalue="192.168.9.45" />
       <parameter name="port" type="int" defaultvalue="6789"/>
    </add>
</localdata>
</DataSource>

I need to add another entry to "localdata" so it would be

 <DataSource>
     <localdata>
        <add context="Localization">
           <parameter name="timeout" type="int" defaultvalue="60"/>
           <parameter name="address" type="string" defaultvalue="192.168.9.45" />
           <parameter name="port" type="int" defaultvalue="6789"/>
        </add>
       <add context="General">
           <parameter name="timeout" type="int" defaultvalue="60"/>
           <parameter name="address" type="string" defaultvalue="192.168.9.478" />
           <parameter name="port" type="int" defaultvalue="5674"/>
        </add>
    </localdata>
    </DataSource>

How would I add this in vbscript?

My Current code

'created xml file object
Set xmlDoc = CreateObject("Msxml2.DOMDocument")

xmlDoc.async = False  
xmlDoc.preserveWhiteSpace= True 
xmlDoc.load("DataConfiguration.xml")

Dim entry

entry = "<add context=""General"">" & _
               <parameter name=""timeout"" type=""int"" defaultvalue=""60""/>" & _ 
               <parameter name=""address"" type=""string"" defaultvalue=""192.168.9.478"" />" & _
               <parameter name=""port"" type=""int"" defaultvalue=""5674""/>"& _
            </add>"

Set NewNode = xmlDoc.createElement(entry)
Set ElemList = xmlDoc.getElementsByTagName("localdata")
ElemList.appendChild(NewNode)

But this give the error

This name may not contain < character" at " Set NewNode = xmlDoc.createElement(entry)

Also the ElemList.appendChild(NewNode) does not work.


Solution

  • XmlDocument.CreateElement accepts three params: a node type, a node name, and a namespace. In your example, since your child element is named "add", it's an element (type==1), and it is part of the global xml namespace, you would call xmlDoc.CreateElement(1, "add", "") .

    That gives you an empty element. To insert the data you want (the Context="General" attribute, and all the child elements), you'd then need to make successive calls to the DOM manipulation methods, to add in each child element, each attribute, and so on. Pretty laborious.

    But you already have the xml fragment as a string. So instead of creating the element using DOM methods, what you can do is create a 2nd XmlDocument and tell it to get its content from the string. Then grab the documentElement from that 2nd doc. Then call appendChild on the appropriate node in first doc, passing the documentElement from the 2nd doc.

    something like this:

    Function GetElementFromXmlString(xmlString)
        Dim doc
        set doc = CreateObject("Msxml2.DOMDocument.6.0")
        doc.async = False
        doc.preserveWhiteSpace= False
        doc.loadXML(xmlString)
        Set GetElementFromXmlString = doc.documentElement
    End Function
    
    Sub Main()
        Set doc1 = CreateObject("Msxml2.DOMDocument.6.0")
        doc1.async = False
        doc1.preserveWhiteSpace= False ' True
        doc1.load("DataConfiguration.xml")
    
        ' generate an Element from an XML string
        Dim xmlString
        xmlString = "<add context=""General"">" & _
                      " <parameter name=""timeout"" type=""int"" defaultvalue=""60""/>" & _
                      " <parameter name=""address"" type=""string"" defaultvalue=""192.168.9.478"" />" & _
                      " <parameter name=""port"" type=""int"" defaultvalue=""5674""/>"& _
                  "</add>"
        Dim newElt
        Set newElt = GetElementFromXmlString(xmlString)
    
        ' get the first child node of type=Element under the document root element in
        ' doc1.  This is not the same as  doc1.documentElement.firstChild.  There can
        ' be text nodes, etc.
        Dim node1
        Set node1 = doc1.documentElement.selectSingleNode("./*[position()=1]")
    
        ' append the element to the node
        node1.appendChild(newElt)
    
        WScript.echo (PrettyPrintXml (doc1))
    End Sub
    
    Main()
    

    ...where the PrettyPrintXml function is defined like this:

    Function PrettyPrintXml(xmldoc)
        Dim reader
        set reader = CreateObject("Msxml2.SAXXMLReader.6.0")
        Dim writer
        set writer = CreateObject("Msxml2.MXXMLWriter.6.0")
        writer.indent = True
        writer.omitXMLDeclaration = True
        reader.contentHandler = writer
        reader.putProperty "http://xml.org/sax/properties/lexical-handler", writer
        reader.parse(xmldoc)
        PrettyPrintXml = writer.output
    End Function
    

    The output of this, for me, is:

    <DataSource>
      <localdata>
        <add context="Localization">
          <parameter name="timeout" type="int" defaultvalue="60"/>
          <parameter name="address" type="string" defaultvalue="192.168.9.45"/>
          <parameter name="port" type="int" defaultvalue="6789"/>
        </add>
        <add context="General">
          <parameter name="timeout" type="int" defaultvalue="60"/>
          <parameter name="address" type="string" defaultvalue="192.168.9.478"/>
          <parameter name="port" type="int" defaultvalue="5674"/>
        </add>
      </localdata>
    </DataSource>