Processing HTML5 in an XML Pipeline

My last blog post was about HTML5 Compare, an online comparison tool for HTML5. The comparison process we used for this relies on an XML pipeline built around DeltaXML’s Core product. In this blog post I describe the solution we used for parsing HTML5 for the pipeline input and serializing to HTML5 from the pipeline output.

Selecting an HTML5 Parser/Serializer

To fit in with existing components we were looking for something that was Java based and also allowed good integration with Saxonica’s Saxon XSLT processor. Another key requirement was that the parser solution should conform to the HTML5 specification. One of the great strengths of HTML is that it is very forgiving when confronted with poorly formed tag content in the source, HTML5 therefore provides a parsing specification for both valid and invalid HTML to ensure that the same DOM is generated for any conforming parser implementation.

Given the above constraints, we selected the Mozilla backed Validator.nu parser, an HTML5 serializer that complements the behaviour of the parser is also included with this component.

DXP Pipeline

Integrating the Parser and Serializer into the pipeline

The sample code excerpt below shows the Java code used to work interoperably between the HTML5 and XML components:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public void compare(InputStream is1, String systemId1, InputStream is2,
                    String systemId2, OutputStream result){
   
    HtmlParser htmlParser= new HtmlParser();
    htmlParser.setErrorHandler(errorHandler);
    org.xml.sax.XMLReader xmlReader= htmlParser;
    
    Processor saxonProcessor= new Processor(true);     
    InputSource in1= new InputSource(is1);
    InputSource in2= new InputSource(is2);
    
    in1.setSystemId(systemId1);
    in2.setSystemId(systemId2);
    
    DocumentBuilder db= saxonProcessor.newDocumentBuilder();
    
    SAXSource saxSource1= new SAXSource(xmlReader, in1);
    SAXSource saxSource2= new SAXSource(xmlReader, in2);
    XdmNode inputNode1= db.build(saxSource1);
    XdmNode inputNode2= db.build(saxSource2);
    
    com.deltaxml.xhtml.XhtmlCompare xhtmlCompare= new XhtmlCompare();
    XdmNode compareOutputNode= xhtmlCompare.compare(inputNode1, inputNode2);
    
    XsltCompiler comp= saxonProcessor.newXsltCompiler();
    
    StreamSource finalXslt= new StreamSource(xsltStringReader);
    XsltExecutable execFilter= comp.compile(finalXslt);
    XsltTransformer transformer= execFilter.load();
    transformer.setInitialContextNode(compareOutputNode);
    
    ContentHandler ch= new HtmlSerializer(result);
    transformer.setDestination(new SAXDestination(ch));
    transformer.transform();
 }

From above, we can see that HTML5Parser implements org.xml.sax.XMLReader, whilst HTML5Serializer implements org.xml.sax.ContentHandler, allowing us make Saxon9 API calls to methods with arguments that implement these interfaces. For each HTML5 input, we can therefore create an XdmNode instance, which is then processed by Core.

The XML output from Core is also an XdmNode instance, this is used to construct the HTML5Serializer object, which in turn is supplied to the setDestination method of the XSLTTransformer instance. Executing the transform() method of this instance (in this case using an XLST identity transform) gives us standard serialized HTML5 output.

Please note that a design constraint required the use of XdmNodes in this particular example, the solution would have been simpler and allowed parallel parsing if we had used two SaxSource instances, with each instance constructed using its own independent XmlReader object.

If you’re interested in more information on the use of SAX interfaces in pipelines, please see Powering Pipelines with JAXP on our Articles and Papers page.

Processing HTML5

Using this HTML5 parser/serializer combination with its SAX API allowed us to conveniently process the HTML5 DOM almost as if we were dealing with XHTML. Once parsed, HTML5 elements are even in the XHTML namespace. You may have noticed from the code extract above that the class that represents the pipeline for Core is XHTMLCompare, this is because we were actually using a customized version of an exsiting pipeline designed for XHTML. There are of course important differences with XHTML even once parsed, one is the absence of default attributes. For example, to use XSLT filters designed for XHTML we needed to add xml:space="preserve" attributes to pre elements.

Conclusion

We found that by selecting standards-based parsing and serializing solutions, processing HTML5 using an XML-based pipeline can be relatively straightforwards. Existing resources for processing XHTML can also be used with only minor adjustments.

Keep Reading

1 reply

Trackbacks & Pingbacks

  1. […] Processing HTML5 in an XML Pipeline (blogs.deltaxml.com) […]

Comments are closed.