Richard Searle's Blog

Thoughts about software

Converting Scala XML into SAX events

Posted by eggsearle on September 2, 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 &lt; <y xmlns:g="http://example.com"  h:b="13"/> </X> </Outside>

 XMLDumper.toXML(xml,xformer)
}
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: