Skip to content

Use Case: Patching XML

do- edited this page Dec 27, 2021 · 6 revisions

Input

Consider the following XML given as a string named xmlSource:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
  <SOAP-ENV:Header/>
  <SOAP-ENV:Body>
    <GetResponseResponse xmlns="urn://some-schema" xmlns:ns0="urn://another-schema">
      <ResponseMessage>
        <Response>
          <OriginalMessageId>00000000-0000-0000-0000-000000000000</OriginalMessageId> <!-- to be substituted -->
          <SenderProvidedResponseData>
<!-- and so on -->

Problem

The goal is to obtain the XML text representing the same document with OriginalMessageId's content replaced with a given id.

Solution

const {XMLReader} = require ('xml-toolkit')

let xmlResult = ''; for await (const node of new XMLReader ().process (xmlSource)) xmlResult += 
    node.isCharacters && node.parent.localName === 'OriginalMessageId' ? id : 
    node.xml

Explanation

First, an XMLReader instance is created. This is a SAX style parser.

The .process (xmlSource) call returns the same object, but emitting the sequence of XMLNodes (in xml-toolkit, it's a SAXEvent's subclass) representing parts of the input XML document.

XMLReader is an object mode Readable stream, of XMLNodes.

In particular, they are available to read with a for await loop, as every Readable stream provides an asynchronous iterator.

Each XMLNode has an xml field: the string representing that node. So, by concatenating all .xmls we'll reconstruct the input XML text.

The last step to solve the problem is to substitute the .xml value of one text node with the arbitary value. The node to replace is detected with:

  • .isCharacters field (same as .type === 'Characters', but less error prone);
  • .parent reference (pointing to the enclosing XMLNode)
    • the latter's .localName.
Clone this wiki locally