XHR, XPath, and custom local XML schema

272 Views Asked by At

Although it's possible, and somewhat common to use XML extensions in (X)HTML5, (e.g. SVGs), parsing a XHTML5 document, extended with a custom local XSD, using XPaths is causing unexpected results.

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html>
<html xmlns:hp="http://homepage.org/homepage" 
      hp:schemaLocation="http://homepage.org ./schemas/homepage.xsd">
  <head>
    ...
  </head>
  <body>
    <hp:homepage title="Homepage">
    <header>
      <h1></h1>
    </header>
    ...

Utilising the usual NSResolver logic causes namespace errors:

  var hpResolver = document.createNSResolver( document.ownerDocument == null ? document.documentElement : document.ownerDocument.documentElement);

And creating custom handlers leads to no errors, but the XPath still doesn't return the expected results:

  var hpResolver = function (prefix) {
    if (prefix === 'hp') {
      return 'http://homepage.org/homepage';
    } else {
      return null;
    }
  };

The XSD is being supplied with the rest of the files, and is being tested on the local file system rather than on a server as an educational exercise.

The XPath query is as follows:

  var path = '/html/body/hp:homepage/header/h1';
  var headTitle = document.evaluate(path, document, hpResolver, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;

The above samples have been written simply as the example.

<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           targetNamespace="http://homepage.org/homepage"
           xmlns="http://homepage.org/homepage"
           elementFormDefault="qualified">

  <xs:element name="homepage">
    <xs:complexType>
      <xs:sequence>
        <xs:attribute name="title" type="xs:string" use="required" />
      </xs:sequence>
    </xs:complexType>
  </xs:element>

</xs:schema>
1

There are 1 best solutions below

0
On BEST ANSWER

I don't think schemas matter at all as the browsers don't have validating parsers with schema support. What matters are namespaces and if you want to use custom elements and namespaces then you need to make sure you use XHTML5 correctly, making sure your elements are in a namespace, making sure you serve up documents as application/xhtml+xml (as only then they are parsed by the browsers XML parser that understands namespaces, a text/html parser is parsed by the HTML5 parser that does not support namespaces).

Based on that I created the example http://home.arcor.de/martin.honnen/html/test2015061502.xhtml which has the code

<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>XMLHttpRequest and DOM Level 3 XPath API test with 'application/xhtml+xml' document</title>
<script type="text/javascript"><![CDATA[
function test() {
  var req = new XMLHttpRequest();
  req.open('GET', 'test2015061501.xhtml', true);
  req.onload = function() {
    var doc = req.responseXML;
    var res = function(prefix) {
      if (prefix === 'xhtml') {
        return 'http://www.w3.org/1999/xhtml';
      }
      else if (prefix === 'hp') {
        return 'http://example.com/hp';
      }
      else {
        return null;
      }
    }
    var path = '/xhtml:html/xhtml:body/hp:homepage/xhtml:header/xhtml:h1';
    var val = doc.evaluate(path, doc, res, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
    document.body.insertAdjacentHTML('beforeEnd', '<p>Found ' + val + '</p>');
  };
  req.send();
}

window.onload = test;
]]></script>
</head>
<body>
<h1>XMLHttpRequest and DOM Level 3 XPath API test with 'application/xhtml+xml' document</h1>
</body>
</html>

and loads http://home.arcor.de/martin.honnen/html/test2015061501.xhtml which has elements in the XHTML namespace and elements in a custom namespace:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:hp="http://example.com/hp" 
      hp:schemaLocation="http://example.com/hp ./schemas/homepage.xsd">
  <head>
    ...
  </head>
  <body>
    <hp:homepage title="Homepage">
      <header>
        <h1>I am a heading</h1>
      </header>
    </hp:homepage>
  </body>
</html>

The XPath 1.0 expression in the first document is /xhtml:html/xhtml:body/hp:homepage/xhtml:header/xhtml:h1 and finds the XHTML h1 HTMLHeadingElement element, both with Firefox 38 as well as with Chrome 43.