Thursday, 1 May 2014

Parsing XML data without vars

Another code snippet - was trying to parse a XML document in Scala without vars... Here is the XML:
<apm>
  <entry>
    <service>GOOGLE</service>
    <id>G-ID</id>
    <pwd>G-PWD</pwd>
    <url>www.google.com</url>
  </entry>
  
  <entry>
    <service>AMAZON</service>
    <id>A-ID</id>
    <pwd>A-PWD</pwd>
    <url>www.amazon.com</url>
  </entry>
</apm>
In the scala code that uses pattern matching and recursion.
package scala.fun

import scala.io.Source
import scala.xml.pull.XMLEventReader
import scala.xml.pull.XMLEvent
import scala.xml.pull.EvElemStart
import scala.xml.pull.EvText
import scala.xml.pull.EvElemEnd
case class Label(val lbl: String)

case class Service(val svr: String)
case class Id(val id: String)
case class Pwd(pwd: String)
case class URL(url: String)

case class Entry(val svr: Service, val id: Id, val pwd: Pwd, val url: URL)

object XmlP {
  def parse(xmlf: String): List[Entry] = {
    def ip(xmlr: XMLEventReader, xmle: XMLEvent, start: Boolean = true, label: Label = Label(""),
           service: Service = Service(""), id: Id = Id(""), pwd: Pwd = Pwd(""), url: URL = URL(""),
           entries: List[Entry] = List()): List[Entry] = {
      xmle match {
        case EvElemStart(pre, lbl, attrs, scope) => ip(xmlr, xmlr.next, true, Label(lbl), service, id, pwd, url, entries)
        case EvText(txt) =>
          if (start)
            label.lbl match {
              case "service" => ip(xmlr, xmlr.next, true, label, Service(txt.trim), id, pwd, url, entries)
              case "id" => ip(xmlr, xmlr.next, true, label, service, Id(txt.trim), pwd, url, entries)
              case "pwd" => ip(xmlr, xmlr.next, true, label, service, id, Pwd(txt.trim), url, entries)
              case "url" => ip(xmlr, xmlr.next, true, label, service, id, pwd, URL(txt.trim), entries)
              case _ => ip(xmlr, xmlr.next, false, label, service, id, pwd, url, entries)
            }
          else ip(xmlr, xmlr.next, false, label, service, id, pwd, url, entries)
        case EvElemEnd(pre, lbl) => {
          val newEntries = if ("entry".equals(lbl)) Entry(service, id, pwd, url) +: entries else entries
          if (xmlr.hasNext) ip(xmlr, xmlr.next, false, label, service, id, pwd, url, newEntries) else entries
        }
        case _ => if (xmlr.hasNext) ip(xmlr, xmlr.next, false, label, service, id, pwd, url, entries) else entries
      } // xmle match
    } //ip
    
    val xml = new XMLEventReader(Source.fromFile(xmlf))
    ip(xml, xml.next)
  }
  
  val result = parse("apm.xml")
  println(s"result: $result")

  def main(args: Array[String]): Unit = {}
}

1 comment:

Hemant said...

Good implementation using classic tail recursion!

Blog Archive