skip to Main Content

I have a xml file:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE agent SYSTEM "http://www.someUrl.com">
<myData>
   <service>
      <description>Description</description>
   </service>
   <courier>
      <listener port="55556"
                />
      <mainService
      name="Some Name"
      port="55555"
      />
   </courier>
</myData>

and I want to get the value of the listener port variable using nodejs.
According to the docu for SaxonJs SaxonJS.XPath.evaluate is the method I need. It requires a query and a document node object. When I convert my xml file into dom object I get the error message : "Uncaught TypeError TypeError: Cannot read properties of undefined (reading ‘length’)"

const saxonJs = require('saxon-js');
const fs      = require('node:fs');
const jsdom = require("jsdom");

var xmlFile   = fs.readFileSync(xmlFilePath, 'utf8');

var doc = new jsdom.JSDOM(xml);
var xpathQuery  = '//listener/@port';
var result      = saxonJs.XPath.evaluate(xpathQuery, doc);

2

Answers


  1. Do you need to use XPath in particular? Since you have JSDOM, I’d just query using a CSS selector:

    import {JSDOM} from "jsdom"; // ^24.0.0
    
    const xml = `<?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE agent SYSTEM "http://www.someUrl.com">
    <myData>
       <service>
          <description>Description</description>
       </service>
       <courier>
          <listener port="55556"
                    />
          <mainService
          name="Some Name"
          port="55555"
          />
       </courier>
    </myData>`;
    
    const dom = new JSDOM(xml, {contentType: "application/xml"});
    const {document} = dom.window;
    const port = document.querySelector("listener").getAttribute("port");
    console.log(port); // => 55556
    

    You can use JSDOM.fromFile to read from disk without using fs (although that’s fine too).

    If you don’t mind querying with CSS, Cheerio provides an even cleaner option:

    const cheerio = require("cheerio"); // ^1.0.0-rc.12
    
    const xml = `...`;
    console.log(cheerio.load(xml, {xml: true})("listener").attr("port"));
    

    If you really want to use XPath, you can do it in JSDOM (note the [@port] change to select by attribute on the listener element):

    const port = document
      .evaluate(
        "//listener[@port]",
        document,
        null,
        9, // FIRST_ORDERED_NODE_TYPE
      )
      .singleNodeValue
      .getAttribute("port");
    

    or selecting the attribute string value directly:

    const port = document
      .evaluate(
        "//listener/@port",
        document,
        null,
        2, // STRING_TYPE
      )
      .stringValue;
    

    Note that using both JSDOM and Saxon is probably unnecessary, since Saxon can parse the XML without support from JSDOM. Pick one or the other.

    I’m not familiar with Saxon, but at the time of writing, JSDOM has 22,000,000 weekly downloads, while saxon-js has 20,000. My instinct is to avoid the dependency on saxon-js if possible. On the other hand, apparently JSDOM XPath has "quite a few issues", so you may have a good reason for pursuing Saxon if it’s a better tool for your particular use case (i.e. you need robust XML support and don’t mind the support/maintenance cost of using a less popular library).

    Login or Signup to reply.
  2. To give you an example for SaxonJS with Node.js:

    const SaxonJS = require("saxon-js");
    
    SaxonJS.getResource({ file: 'input-sample.xml', type: 'xml' })
    .then(doc => { console.log(SaxonJS.XPath.evaluate('//listener/@port/xs:integer(.)', doc)) });
    

    Of course you can also try to select the @port attribute node and output its value with e.g.

    const SaxonJS = require("saxon-js");
    
    SaxonJS.getResource({ file: 'saxon-js-input-sample1.xml', type: 'xml' })
    .then(doc => { var port = SaxonJS.XPath.evaluate('//listener/@port', doc); if (port !=null) console.log(port.value) });
    

    Or in the ESM module world as

    import SaxonJS from 'saxon-js';
    
    const doc = await SaxonJS.getResource({ file: 'saxon-js-input-sample1.xml', type: 'xml' });
    
    var port = SaxonJS.XPath.evaluate('//listener/@port', doc); 
    
    if (port !=null) 
      console.log(port.value);
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search