Scala XML & StAX

Posted 2011年1月15日 by

StAX(Streaming API for XML)

「Scala には XML に対するファーストクラスのサポートが最初から組み込まれているため、XML ストリングの作成や、プログラムによる DOM ツリーの作成が必要ありません。」
と説明されているのですが、XhtmlParserだとDOMよりもメモリーを使っているのではないと思う次第です。いまどきメモリーを気にすることもないかもしれませんが、でも気になります。

データ交換にXMLを使うことが多いのでDOMよりもSAXが好みです。
Javaのときに使いなれたStAXコードをScalaに移植してみました。
このコードは、XMLを調べて構文をイベントで処理します。

scala.xml.pull.XMLEventReader を使った処理も書いてみました。
StAXコードと同じプル型。コードはすっきりしています。

package Hello
import java.io.{BufferedInputStream,BufferedReader,ByteArrayInputStream,FileInputStream,IOException}
import java.io.InputStreamReader
import java.util.{Iterator,Stack}
import org.xml.sax.InputSource
import org.xml.sax.helpers.DefaultHandler
import org.xml.sax.Attributes
import javax.xml.parsers.SAXParser
import javax.xml.parsers.SAXParserFactory
import javax.xml.namespace.QName
import javax.xml.stream.{XMLEventReader,XMLInputFactory,XMLStreamException,XMLStreamReader,XMLStreamConstants}
import javax.xml.stream.events.{Attribute,Characters,EndElement,StartElement,XMLEvent}
import scala.collection.mutable.ArrayBuffer
import scala.collection.JavaConversions._
import scala.xml._
import scala.xml.pull._
import scala.io.Source
import scala.collection.mutable.Stack

object Xmlcheck {
	def XmlCheck(xml:NodeSeq) :Boolean = {
		XmlStringCheck(xml.toString)
	}
	def XmlStringCheck(xmlString:String) :Boolean = {
		if(xmlString == null)
			return false
		var bt:Array[Byte] = xmlString.getBytes("UTF-8")
		val in = new ByteArrayInputStream(bt)

		val factory:XMLInputFactory = XMLInputFactory.newInstance
		val reader:XMLStreamReader = factory.createXMLStreamReader(in, "UTF-8")
		while(true) {
			var eventType = reader.getEventType
			if(eventType == XMLStreamConstants.ATTRIBUTE){
				println("eventType: ATTRIBUTE")
			}else if(eventType == XMLStreamConstants.CDATA){
				 println("eventType: CDATA")
			}else if(eventType ==  XMLStreamConstants.CHARACTERS){
				println("eventType: CHARACTERS: "+reader.getText.trim)
			}else if (eventType ==  XMLStreamConstants.COMMENT){
				println("eventType: COMMENT")
			}else if (eventType ==  XMLStreamConstants.DTD){
				println("eventType: DTD")
			}else if (eventType ==  XMLStreamConstants.END_DOCUMENT){
				println("eventType: END_DOCUMENT")
			}else if (eventType ==  XMLStreamConstants.END_ELEMENT){
				println("eventType: END_ELEMENT: "+reader.getName)
			}else if(eventType ==  XMLStreamConstants.ENTITY_DECLARATION){
				println("eventType: END_ELEMENT")
			}else if(eventType ==  XMLStreamConstants.ENTITY_REFERENCE){
				println("eventType: ENTITY_REFERENCE")
			}else if(eventType ==  XMLStreamConstants.NAMESPACE){
				println("eventType: NAMESPACE")
			}else if(eventType ==  XMLStreamConstants.NOTATION_DECLARATION){
				println("eventType: NOTATION_DECLARATION")
			}else if(eventType ==  XMLStreamConstants.PROCESSING_INSTRUCTION){
				println("eventType: PROCESSING_INSTRUCTION")
			}else if(eventType ==  XMLStreamConstants.SPACE){
				println("eventType: SPACE")
			}else if(eventType ==  XMLStreamConstants.START_DOCUMENT){
				println("eventType: START_DOCUMENT")
			}else if(eventType ==  XMLStreamConstants.START_ELEMENT){
				println("eventType: START_ELEMENT: "+reader.getName)
			}else {
				println("eventType: Other: "+eventType)
			}
			if(reader.hasNext) {
				reader.next
			} else {
				reader.close
				in.close
				return true
			}
		}
		false
	}
	
	def XmlStringCheck2(xml:String) :Boolean = {
		val src = Source.fromString(xml)
		val er = new scala.xml.pull.XMLEventReader(src)
		val stack = scala.collection.mutable.Stack[scala.xml.pull.XMLEvent]()
		def iprintln(s:String) = println((" " * stack.size) + s.trim)
		while (er.hasNext) {
		  er.next match {
		    case x @ EvElemStart(_, label, _, _) =>
		      stack push x
		      iprintln("EvElemStart <" + label + " ...>")
		    case EvElemEnd(_, label) => 
		      iprintln("EvElemEnd </" + label + ">")
		      stack pop;
		    case EvText(text) => 
		      iprintln(text) 
		    case EvEntityRef(entity) => 
		      iprintln(entity) 
		    case _ => // ignore everything else
		  }
		}
		true
	}
	
	def main(args : Array[String]) : Unit = {
		println("result=" + XmlCheck(<HTML><BODY>abcd12345</BODY></HTML>))
		println("result=" + XmlStringCheck("<H><B>abcd12345</B></H>"))
		println("result=" + XmlStringCheck2("<HX><BC>abcd12345</BC></HX>"))
	}
}


実行した結果です。

eventType: START_DOCUMENT
eventType: START_ELEMENT: HTML
eventType: START_ELEMENT: BODY
eventType: CHARACTERS: abcd12345
eventType: END_ELEMENT: BODY
eventType: END_ELEMENT: HTML
eventType: END_DOCUMENT
result=true
eventType: START_DOCUMENT
eventType: START_ELEMENT: H
eventType: START_ELEMENT: B
eventType: CHARACTERS: abcd12345
eventType: END_ELEMENT: B
eventType: END_ELEMENT: H
eventType: END_DOCUMENT
result=true
 EvElemStart <HX ...>
  EvElemStart <BC ...>
  abcd12345
  EvElemEnd </BC>
 EvElemEnd </HX>
result=true

Post Details

  • Post Title: Scala XML & StAX
  • Author: admin
  • Filed As: Scala
  • Tags:
  • You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

コメントを残す