2
votes

Is there a simple method to locate an XML node by its attribute in Google Apps Script? Here's an XML snippet:

<hd:components>
    <hd:text name="ADM Custom admissions TE">
        <hd:prompt>Admission</hd:prompt>
        <hd:columnWidth widthType="minimum" minWidth="100"/>
    </hd:text>
    <hd:text name="ADM Insufficient heat end date TE">
        <hd:prompt>To</hd:prompt>
    </hd:text>
    <hd:text name="ADM Insufficient heat start date TE">
        <hd:prompt>From</hd:prompt>
    </hd:text>
    <hd:text name="ADM Third party payment period TE">
        <hd:defMergeProps unansweredText="__________"/>
        <hd:prompt>When (date or period)?</hd:prompt>
    </hd:text>

For purposes of the XML file I'm trying to parse, the "name" attribute is a unique identifier, while what GAS thinks is the "name" for purposes of the XmlService.Element.getChild(name) method ("text" for each node shown in this snippet) is a non-unique classifier for the type of node. I'd like to be able to write a function to retrieve a specific node from this XML file with only the name attribute. XMLPath notation in other languages has this capability using the [@ notation. Is there a way to do it in GAS, or do I need to write a function that walks through the XML until it finds a node with the right name attribute, or store it in some different type of data structure for fast searching if the XML file is sufficiently large?

Here's the snippet I started writing: it's fine if there's no built-in function, I just wondered if there was a better/faster way to do this. My function isn't so efficient, and I wondered if the XmlService had a more efficient internal data structure it's using to speed up searching. My approach is just to loop through all of the element's children until there's a match.

function getComponentFromXML(xml,name) { 
  for (var i = 0; i < xml.length; i++) { 
    var x = xml[i];
    var xname = x.getAttribute('name').getValue();
    if (xname == name) {
      return getComponentAttributes(x);
    }
  }
}
1
Did you already check the official documentation of Google Apps Script about XmlService and the attribute? - KENdi
I did, and I didn't see a method that's already defined to search based on attribute. It's not a big function, I just worry about doing it a non-standard an unoptimized way, especially as it's available in Xpath and other XML implementations in different languages. - Quinten

1 Answers

0
votes

There is no built-in search, so the only way is to read the list of elements looking for the one with the desired value of attribute 'name'. If elements is an array of elements to search through, you can do

var searchResults = elements.filter(function (e) {
  return e.getAttribute('name') && e.getAttribute('name').getValue() == searchString;
});

(Both checks are needed to avoid an error when there is no 'name' attribute at all.)

How to obtain such an array elements may depend on XML document. If, as in your example, the elements to search are the immediate children of the root element, then

var doc = XmlService.parse(xmlString);
var elements = doc.getRootElement().getChildren();

would be a quick and easy way to do this.

In general, to get all elements without recursion, the getDescendants method can be used. It returns an array of Content object, which can be filtered down to Element objects:

var elements = doc.getDescendants().filter(function (c) {
  return c.getType() == XmlService.ContentTypes.ELEMENT;
}).map(function (c) {
  return c.asElement();
});