netsuitesuitescriptsuitescript2.0

How to extract all xml data (including xml tags) using Suitescript


Hope you can help. I am currently assessing the best way to extract ALL 'OrderLevel' tags (this would include both tags and values) from an XML file in SuiteScript 2.1.

Here's an example snippet:

<RootLevel>
  <OrderLevel>
    <PurchaseOrderNumber>123</PurchaseOrderNumber>
      <item>fridge</item>
    <PurchaseOrderDate/>
    <Vendor>abc</Vendor>
  </OrderLevel>
  <OrderLevel>
    <PurchaseOrderNumber>456</PurchaseOrderNumber>
      <item>cupboard</item>
    <PurchaseOrderDate/>
    <Vendor>def</Vendor>
  </OrderLevel>
</RootLevel>

This is a very simple example of course. I was considering using a REGEX pattern, but I would rather a method that is considered best practice.

The whole goal of this exercise is to combine all tags from multiple XML files into one single XML file. To be clear, I do not want just the values, but also the tags.

Thoughts?

EDIT: Taking advice frim @Krypton, here is the SuiteScript code I have attempted to write:

// file to copy from
            var childFile = file.load({id: value.fileId});
            var childXmlContents = childFile.getContents();
            log.debug('childXmlContents',childXmlContents);
            var childXmlData = xml.Parser.fromString({text: childXmlContents});

 //file to copy to
            var parentFile = file.load({id: value.parentFileId});
            var parentXmlContents = parentFile.getContents();
            log.debug('parentXmlContents',parentXmlContents);
            var parentXmlData = xml.Parser.fromString({text: parentXmlContents});

//Capture root node from parent file: We aim to copy node from child file.
           const parentRootNode = parentXmlData.getElementsByTagName('RootLevel')[0];

//Capture orderLevel node from child file
           var orderXMLElement = childXmlData.getElementsByTagName({tagName: 'OrderLevel'});

//Loop through each orderLevel node of child file, COPY node, then append to root of parent file    
           for (var i = 0; i < orderXMLElement.length; i++) {
                var x = childXmlData.getElementsByTagName('OrderLevel')[i];
                var getCloneNode = x.cloneNode(true);
                parentRootNode.appendChild(getCloneNode);
           }

//Return parent XML as string and save file
           const newFileStr = xml.Parser.toString({document: parentXmlData});
           const xmlFile = createXMLFile(parentFile.name, newFileStr, 6485323);
           const xmlFileId = xmlFile.save();

The latest error I am receiving: SSS_XML_DOM_EXCEPTION WRONG_DOCUMENT_ERR: A node is used in a different document than the one that created it.

Ideas on what I could be doing wrong?


Solution

  • You can use the N/xml module to manipulate XML files. A simple example of this is as follows - you would need to handle the loading of the files separately, this just demonstrates the XML side of it:

    require(['N'], function(N) {
    for(var n in N){window[n] = N[n];};
    
        let p = xml.Parser;
        let xmlString = `<RootLevel>
      <OrderLevel>
        <PurchaseOrderNumber>123</PurchaseOrderNumber>
          <item>fridge</item>
        <PurchaseOrderDate/>
        <Vendor>abc</Vendor>
      </OrderLevel>
      <OrderLevel>
        <PurchaseOrderNumber>456</PurchaseOrderNumber>
          <item>cupboard</item>
        <PurchaseOrderDate/>
        <Vendor>def</Vendor>
      </OrderLevel>
    </RootLevel>`;
        let xmlString2 = `
        <RootLevel>
      <OrderLevel>
        <PurchaseOrderNumber>4567</PurchaseOrderNumber>
          <item>computer</item>
        <PurchaseOrderDate/>
        <Vendor>xyz</Vendor>
      </OrderLevel>
      <OrderLevel>
        <PurchaseOrderNumber>6542</PurchaseOrderNumber>
          <item>mouse</item>
        <PurchaseOrderDate/>
        <Vendor>pqr</Vendor>
      </OrderLevel>
    </RootLevel>
        `
        let xmlDoc = p.fromString({
            text: xmlString
        });
        let xmlDoc2 = p.fromString({
            text: xmlString2
        });
        let xmlDocs = [xmlDoc,xmlDoc2];
        let orderNodes = [];
        xmlDocs.forEach( x => {
            let orderNodesDoc = xml.XPath.select({
                node:x,
                xpath: '//OrderLevel'
            });
            orderNodes.push.apply(orderNodes, orderNodesDoc);
        })
        console.log(orderNodes);
        let newXmlDoc = p.fromString({
            text: '<NewRoot></NewRoot>'
        });
        let newRootNode = newXmlDoc.getElementsByTagName('NewRoot')[0];
            
        console.log('newXmlDoc',newXmlDoc);
        console.log(orderNodes.length);
        for (i=0;i<orderNodes.length;i++) {
            let orderNode = newXmlDoc.importNode(orderNodes[i], true);
            newRootNode.appendChild(orderNode);
        }
    
        console.log(newXmlDoc);
        let newXmlString = p.toString({
            document: newXmlDoc
        });
        console.log('newXmlString', newXmlString);
        let oldXmlString = p.toString({
            document: xmlDoc
        });
        console.log('oldXmlString', oldXmlString);
    })
    

    Note that this is using the require function - you can copy and paste it into your browser's dev tools console and run it directly, but will need to modify it to use define for your production script.

    HTH!