Search code examples
google-apps-scriptgoogle-drive-apixml-parsing

XmlService error getting nested child element (Google AppScript)


Looking for assistance with an error I'm getting when attempting to get a nested node value in an XML file using XmlService and Google's AppScript. The node name is "asciiformat" and its position in the XML file is according to below (this is a simplified version; find full XML structure below AppScript code):

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<feed version="0.3" xmlns="http://purl.org/atom/ns#">
    <entry>
        <tes:ftpjob xmlns:tes="http://www.software.com">
            <tes:asciiformat>N</tes:asciiformat>

Here's the error from AppScript:

TypeError: ftpJobInfo.getChildren is not a function
doGet   @ Code.gs:17

Here's the code:

function doGet(e) {
  let fileID = '1qHbA97eVaJtPWU3g_f9dEtwg-rlvkpc2';
  url = DriveApp.getFileById(fileID).getBlob().getDataAsString();  
  let document = XmlService.parse(url);

  let root = document.getRootElement();

  let ns = root.getNamespace();
  let entries = root.getChildren("entry", ns);
  let entry, ftpJobInfo, ftpJobInfoDetails;

  let id, title, asciiFormat;
  for (let counter = 0; counter < entries.length; counter++) {
    entry = entries[counter];
 
    ftpJobInfo = entry.getContent(8);
    ftpJobInfoDetails = ftpJobInfo.getChildren('tes:ftpjob');
    
    asciiFormat = ftpJobInfoDetails.getContent(0).getValue();

    Logger.log("prayer hands");
  }
}

Here's the full XML file:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<feed version="0.3" xmlns="http://purl.org/atom/ns#">
    <title>tes-6.5/tes:FTPJob.getList</title>
    <category term="0" scheme="row" label=""/>
    <category term="0" scheme="level" label=""/>
    <category term="111111111111111111111111" scheme="securitypolicy" label=""/>
    <category term="000000000000000000000000" scheme="customsecuritypolicy" label=""/>
    <generator uri="http://www.software.com">TES</generator>
    <modified>2022-08-04T15:41:11.027-06:00</modified>
    <entry>
        <id>9966</id>
        <title>01 PMI SFTP</title>
        <category term="0" scheme="row" label=""/>
        <category term="0" scheme="level" label=""/>
        <category term="111111111111111111111111" scheme="securitypolicy" label=""/>
        <category term="000000000000000000000000" scheme="customsecuritypolicy" label=""/>
        <source>com.software.FTPJobView</source>
        <tes:ftpjob xmlns:tes="http://www.software.com">
            <tes:asciiformat>N</tes:asciiformat>
            <tes:ftphost>1.1.1.1:722</tes:ftphost>
            <tes:filename>*.txt</tes:filename>
            <tes:ftpoperation>2</tes:ftpoperation>
            <tes:ftpprotocol>sftp</tes:ftpprotocol>
            <tes:ftpuserid>25</tes:ftpuserid>
            <tes:localpath>\\localpath</tes:localpath>
            <tes:newfilename/>
            <tes:replacefiles>N</tes:replacefiles>
            <tes:userpasswordauthentication>Y</tes:userpasswordauthentication>
            <tes:id>9966</tes:id><tes:typename>FTPJOB</tes:typename>
            <tes:name>01 PMI SFTP</tes:name><tes:parentname>\location\location\location</tes:parentname>
            <tes:active>Y</tes:active>
            <tes:ownerid>16</tes:ownerid>
            <tes:agentid>65</tes:agentid>
            <tes:command>\\location</tes:command>
            <tes:createtime>2012-08-08T11:05:27-0600</tes:createtime>
            <tes:dependencylogic>1</tes:dependencylogic>
            <tes:fullpath>\location\location\location</tes:fullpath>
            <tes:lastchangetime>2019-07-19T17:27:42-0600</tes:lastchangetime>
            <tes:runuserid>25</tes:runuserid>
        </tes:ftpjob>
    </entry>
</feed>

Solution

  • In your situation, the structure of XML data has already been known. When this situation is used, how about the following modification?

    Modified script:

    From:

    let document = XmlService.parse(url);
    
    let root = document.getRootElement();
    
    let ns = root.getNamespace();
    let entries = root.getChildren("entry", ns);
    let entry, ftpJobInfo, ftpJobInfoDetails;
    
    let id, title, asciiFormat;
    for (let counter = 0; counter < entries.length; counter++) {
      entry = entries[counter];
    
      ftpJobInfo = entry.getContent(8);
      ftpJobInfoDetails = ftpJobInfo.getChildren('tes:ftpjob');
      
      asciiFormat = ftpJobInfoDetails.getContent(0).getValue();
    
      Logger.log("prayer hands");
    }
    

    To:

    const search = "asciiformat"; // This is from your question.
    const root = XmlService.parse(url).getRootElement();
    const ns = XmlService.getNamespace("tes", "http://www.software.com");
    const value = root.getChild("entry", root.getNamespace()).getChild("ftpjob", ns).getChild(search, ns).getValue();
    console.log(value) // N is returned.
    
    • When url is your showing XML data, when you run this modified script, you can see the value of N in the log.

    Note:

    • In your showing script, I think that ftpJobInfo = entry.getContent(8); has no value. By this, I think that this might be the reason for your issue. And, from your showing XML data, the tag of entry is only one. So, in this case, I thought that getChildren is not required to be used. And, from your XML data, it seems that the namespace of ftpjob is tes. This can be used for retrieving the tag of asciiformat. So, I proposed the above modification.

    • As another direction, when this benchmark is considered, I thought that the following sample script might be able to be also used.

        const res = url.match(/<tes:asciiformat>(.+)<\/tes:asciiformat>/);
        console.log(res && res[1]); // N is returned.
      

    Reference: