Getting Scala to process XInclude statements in XML files
I'm working with a number of XML files right now, and decided to use XInclude to enable one XML file to reference (and include) another one.
Here is an example. First, here is employee.xml:
<?xml version="1.0" encoding="UTF-8"?> <employeeInfo xmlns:xi="http://www.w3.org/2001/XInclude"> <xi:include href="common.xml" xpointer="element(/1/1)"/> <name>John Doe</name> </employeeInfo>
Which you can see uses the xi:include element to reference the following file common.xml:
<?xml version="1.0" encoding="UTF-8"?> <commonEmployeeInfo> <company>Acme</company> </commonEmployeeInfo>
We can then use xmllint to process the include for us:
xmllint --xinclude employee.xml
Which produces:
<?xml version="1.0" encoding="UTF-8"?> <employeeInfo xmlns:xi="http://www.w3.org/2001/XInclude"> <company>Acme</company> <name>John Doe</name> </employeeInfo>
So, now that we've got our employee.xml file setup, and referencing the common xml file, I wanted to write Scala code to load in the employee.xml file and process the XInclude element like xmllint did for us.
By default XML.Load doesn't process XInclude statements, here is how to do it:
import scala.xml._
import scala.xml.parsing._
import scala.xml.include._
import org.xml.sax.InputSource
import javax.xml.parsers.SAXParser
import javax.xml.parsers.SAXParserFactory
import javax.xml.validation.Schema
import javax.xml.validation.ValidatorHandler
import org.xml.sax.XMLReader
/**
* A class which makes it possible to turn on XInclude processing of XML files
*
* E.g. one XML file can include another
*
* Example usage:
*
* val is = new InputSource(new java.io.FileReader("../ontology/DigitalCamerasOntology.xml"))
* is.setSystemId("../ontology/")
* val xml = new XIncludeAwareFactoryAdapter().loadXML(is)
*
*/
class XIncludeAwareFactoryAdapter extends NoBindingFactoryAdapter {
override def loadXML(source: InputSource) = {
// create parser
val f = SAXParserFactory.newInstance
f.setNamespaceAware(true) // needed otherwise our xincludes don't seem to get processed
f.setXIncludeAware(true)
val parser = f.newSAXParser
val xr = parser.getXMLReader
xr.setContentHandler(this) // needed otherwise rootElem is null
// parse file
scopeStack.push(TopScope)
xr.parse(source)
scopeStack.pop
rootElem.asInstanceOf[Elem]
}
def loadXMLFromFile( filename: String ) = {
val inputSource = new InputSource(new java.io.FileReader(filename))
// Find the path of the file so we can process XIncludes
val path = new java.io.File(filename).getPath
inputSource.setSystemId(path)
loadXML(inputSource)
}
}
You can then use it like this:
val xml = new XIncludeAwareFactoryAdapter().loadXMLFromFile(filename)
Comments [0]