String.asInstanceOf[scala.xml.Elem]

Up until a few weeks ago I was mostly using Scala to read Json data, but the time came when I had to read some XML. I already had a method to read the Json output from a web service, which returns a StringBuilder. So I thought that I could simply use: (This is just a simplified code sample)

println(parseXML(jsonAsString.asInstanceOf[scala.xml.Elem]))

package com.fmpwizard.examples

import scala.xml._ 

object Main{
  def main(args: Array[String])= {
    def getJsonAsString(url: String): StringBuilder= {
      new StringBuilder( """ <doc>   <node>info</node> </doc> """)
    }
    
    def parseXML(xmlIn: scala.xml.Elem): String= {
      (xmlIn \ "node").text
    }
    val jsonAsString= getJsonAsString("http://some.here.com").toString
    
    println(parseXML(jsonAsString.asInstanceOf[scala.xml.Elem]))
   }
}

I then run:

$ scalac StringToElem.scala

And it compiled just fine, to my surprise, when I tried to run:

$ scala com.fmpwizard.examples.Main

I got this error message:

java.lang.ClassCastException: java.lang.String cannot be cast to scala.xml.Elem
at com.fmpwizard.examples.Main$.main(StringToElem.scala:23)
at com.fmpwizard.examples.Main.main(StringToElem.scala)         
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)         
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)         
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)         
at java.lang.reflect.Method.invoke(Method.java:597)         
at scala.tools.nsc.ObjectRunner$$anonfun$run$1.apply(ObjectRunner.scala:75)         
at scala.tools.nsc.ObjectRunner$.withContextClassLoader(ObjectRunner.scala:49)         
at scala.tools.nsc.ObjectRunner$.run(ObjectRunner.scala:74)         
at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:154         
at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala) 

What do I do in cases like this? Google to the rescue, well, not so this time. I was mostly searching for a way to convert a String to xml.Elem and I just could not find any example, I searched the mailing list archives and nothing. I found how to convert from xml.Elem to String, using the .toString method inherited form Node but that was not what I needed

I was surely not the first person trying to convert a String to XML, but I just didn't know what else to try, I kept reading the scaladoc but I couldn't find anything there either. I even remembered reading about scala and xml on the book Programming Scala but I somehow missed the solution to my problem.

The solution.

import scala.xml._ 
XML.loadString(stringVal)

And of course, after I found XML.loadString(), I also saw it on the scaladoc pages. I hope that the next person that tries to do String.asInstanceOf[xml.Elem] find this post and saves a few hours/days of trying different things.

Just for completeness, this is the full object so that you can play around too:

println(parseXML(jsonAsString.asInstanceOf[scala.xml.Elem]))
    
package com.fmpwizard.examples
import scala.xml._
object Main{
  def main(args: Array[String])= {
    def getJsonAsString(url: String): StringBuilder= {
      new StringBuilder( """ <doc>   <node>info</node> </doc> """)
    }
    def parseXML(xmlIn: scala.xml.Elem): String= {
      (xmlIn \ "node").text
    }
    val jsonAsString= getJsonAsString("http://some.here.com").toString
    println(parseXML(XML.loadString(jsonAsString)))
  }
}

Comments copied from old blog:

robbbminson (Twitter) responded:
Whilst this is great, the loadString function actually seems to want to do a full SAX parse, validating the string. So a simple snippet of the form "<foo>content</foo>" will actually fail, even though it's a totally valid Elem. Any idea how to get round this?
Aug 7 2011, 7:16 PM
Diego Medina responded:
I get this using scala 2.8.1
scala> val x= XML.loadString("<foo>content</foo>")
x: scala.xml.Elem = <foo>content</foo>

so it works just fine, unless I'm missing something

Mar 15 2012, 2:36 PM
David Leppik responded:
If you're getting the error "The markup in the document following the root element must be well-formed" then it's because the parser expects a single root element. "<foo />" works, but "<foo /><foo />" gives that error.
Apr 24 2012, 8:05 AM
bastl responded:
the link to api is broken!
Apr 24 2012, 10:22 AM
Diego Medina responded:
Thanks, it is fixed now.