code

Its all just ones and zeroes isn't it? 
Filed under

xml

 

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)


Filed under  //   sax   scala   xinclude   xml  

Comments [0]

Generating simple XML from a Scala List[T] using XStream

I started working with XStream as a tool to quickly generate XML and/or JSON from Scala objects, it looks very powerful, but unfortunately needs some tweaking to produce simple and clean XML.

This code:



println(new XStream.toXML(List(1,2,3)))


produces this XML:

<scala.coloncolon serialization="custom">
  <unserializable-parents/>
  <scala.coloncolon>
    <int>1</int>
    <int>2</int>
    <int>3</int>
    <scala.ListSerializeEnd/>
  </scala.coloncolon>
</scala.coloncolon>

Instead I wanted something like this:



<list>
  <int>1</int>
  <int>2</int>
  <int>3</int>
</list>

Turns out this is possible, by writing your own custom converter:

Here's the code:



import com.thoughtworks.xstream.converters._
import com.thoughtworks.xstream.converters.collections._
import com.thoughtworks.xstream._
import com.thoughtworks.xstream.mapper._
import com.thoughtworks.xstream.io._

class ListConverter( _mapper : Mapper )  extends AbstractCollectionConverter(_mapper) {
  def canConvert( clazz: Class[_]) = {       
    // "::" is the name of the list class, also handle nil
    classOf[::[_]] == clazz || classOf[scala.Nil$] == clazz
  }
  
  def marshal( value: Any, writer: HierarchicalStreamWriter, context: MarshallingContext) = {
    val list = value.asInstanceOf[List[_]]
    for ( item <- list ) {      
      writeItem(item, context, writer)
    }
  }
  
  def unmarshal( reader: HierarchicalStreamReader, context: UnmarshallingContext ) = {
    var list : List[_] = Nil 
    while (reader.hasMoreChildren()) {
      reader.moveDown();
      val item = readItem(reader, context, list);
      list = list ::: List(item) // be sure to build the list in the same order
      reader.moveUp();
    }
    list
  }
}

object ListConverter {
  def configureXStream( stream: XStream ) = {
    stream.alias("list", classOf[::[_]])
    stream.alias("list", classOf[scala.Nil$])
    stream.registerConverter( new ListConverter(stream.getMapper) )        
  }
}


Filed under  //   scala   xml   xstream  

Comments [0]