Richard Searle

home

Converting Scala XML into SAX events

02 Sep 2010

This object generates SAX events from Scala XML to the provided ContentHandler. If the target also implements LexicalHandler then comments are included.
import scala.xml._
import org.xml.sax.ext.LexicalHandler
import org.xml.sax._
import org.xml.sax.helpers._

import scala.xml.Utility._

object XMLDumper{

   def toXML(
    x: Node,
    target: ContentHandler,
    pscope: NamespaceBinding = TopScope)
  {
    val lexical = if(target.isInstanceOf[LexicalHandler]) target.asInstanceOf[LexicalHandler] else null

     target.startDocument

    x match {
      case c: Comment => if (lexical != null) lexical.comment(c.commentText.toCharArray,0,c.commentText.length)
      case a: Atom[_] =>  { val s = a text; target.characters(s.toCharArray,0,s.length) } 
      case e: EntityRef =>  { val s = e text; target.characters(s.toCharArray,0,s.length) } 
      case  p:ProcInstr => target.processingInstruction(p.target,p.proctext)
      case g: Group =>
        g.nodes foreach {toXML(_, target, x.scope)}
      case _  =>
        val uri = x.namespace
        val localName = x.label
        val qName = if(x.prefix == null) localName else x.prefix + ":" + localName

        val atts = new AttributesImpl() 
        if(x.attributes != null) toXML(x.attributes,atts)
        
        startPrefix(x.scope,pscope,target)
        target.startElement(uri,localName,qName,atts)  
          x.child foreach {toXML(_, target, x.scope)}
        target.endElement(uri,localName,qName) 
        endPrefix(x.scope,pscope,target)  
    }
    target.endDocument
  }

  private def startPrefix(ns:NamespaceBinding,stop:NamespaceBinding,target:ContentHandler){
     if(ns ne stop){
        target.startPrefixMapping(if(ns.prefix==null) "" else ns.prefix,ns.uri);
         startPrefix(ns.parent, stop,target)
     }
  }

  private def endPrefix(ns:NamespaceBinding,stop:NamespaceBinding,target:ContentHandler){
     if(ns ne stop){
       target.endPrefixMapping(if(ns.prefix==null) "" else ns.prefix);
       endPrefix(ns.parent, stop,target)
     }
  }

  private def toXML(att:MetaData,atts:AttributesImpl){
     att match  {
        case pa:PrefixedAttribute =>  atts.addAttribute(null,pa.key,pa.pre+":"+pa.key,"CDATA",toString(pa.value))
        case ua:UnprefixedAttribute =>  atts.addAttribute(null,ua.key,ua.key,"CDATA",toString(ua.value))
        case Null =>  //ignored
     }
     if(att hasNext)
       toXML(att.next,atts)
  }

  private def toString(ns:Seq[Node]) = {
     val sb = new StringBuilder
     ns foreach {_ match { 
          case a: Atom[_] => sb.append(a.text)
          case e:EntityRef => sb.append(e.text)
        }
      }
     sb.toString
  }

} 
With example usage
import java.io._
import javax.xml.parsers._
import javax.xml.transform._
import javax.xml.transform.sax._
import javax.xml.transform.stream._

object XMLDumperDriver extends Application{

val transformerFactory = (TransformerFactory.newInstance).asInstanceOf[SAXTransformerFactory]
val xformer = transformerFactory.newTransformerHandler
xformer.setResult(new StreamResult(System.out))

  val xml = <Outside> <Y xmlns="http://y"/> <X a="12" c="21" xmlns:h="http://com.com" xmlns:q="http://q"  >xxx &amp;lt; <y xmlns:g="http://example.com"  h:b="13"/> </X> </Outside>

 XMLDumper.toXML(xml,xformer)
}