Scala and XML - Part 1
As you know, Scala has a great syntax for XML. Here are some lessons from working with it. Here is a yet another Scala XML sample which creates html.
import scala.xml.XML._
import scala.xml.NodeBuffer
import scala.xml.dtd.{DocType, PublicID}
object ScalaHtml {
val words = List("one", "two", "three")
val url = "http://www.scala-lang.org/"
def main(args : Array[String]) : Unit = {
val page =
<html>
<a href={url}>Scala</a>
<h1>My words</h1>
<ul>{listOfWords(words)}</ul>
</html>;
save("save.html", page)
val doctype = DocType("html",
PublicID("-//W3C//DTD XHTML 1.0 Strict//EN",
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"),
Nil)
saveFull("save.full.html", page, false, doctype)
}
def listOfWords(words: List[String]) = {
val result = new NodeBuffer
for(word <- words) {result &+ (<li>{word}</li>)}
result
}
}
The output is
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">Where the first line (with the DOCTYPE) will be generated when using the second save option.
<html>
<a href="http://www.scala-lang.org/">Scala</a>
<h1>My words</h1>
<ul><li>one</li><li>two</li><li>three</li></ul>
</html>
Notes:
[1] When using a variable as an attribute value (see the <a> tag) you must not wrap it with double quotes. The underlying code is doing something like this:
scala.xml.MetaData $md = new UnprefixedAttribute("href", url(), $md);
Object _tmp1 = null;
NodeBuffer $buf = new NodeBuffer();
$buf.$amp$plus(new Elem(null, "a", $md, Predef$.MODULE$.$scope(), $buf));[2] You can't create a partial node. I.e. you can't create the begin tag of a node in one command, then do some other stuff and at the end close it. Scala node creation is atomic and immutable, if you wish to embed on the fly date then it must be simple text or Node* (for example a single Node or NodeBuffer). You can't have any statements int the curly brackets though you may have a call to a method that does that in its own block.
Addendum [3] An emphasis on the string as a value in the curly brackets. Some (like myself) placed there an Int or other primitives expecting something like string concatenation. Well, it won't work and you'll get this error:
error: overloaded method constructor UnprefixedAttribute with alternatives [scalac](String,Option[Seq[scala.xml.Node]],scala.xml.MetaData)scala.xml.UnprefixedAttribute(String,String,scala.xml.MetaData)scala.xml.UnprefixedAttribute (String,Seq[scala.xml.Node],scala.xml.MetaData)scala.xml.UnprefixedAttribute cannot be applied to (java.lang.String,Int,scala.xml.MetaData)
[scalac] Cluster #{summary.id}
[scalac] ^







6 comments:
For your list of words, you can even use the map function on List and do away with your listOfWords function:
<ul>{words.map(_ => <li>{_}</li>)}</ul>
In that case, we're using map to transform a List[String] to a List[Node].
Yes, even better!
By the way, your sample needs a bit of a touch since it does not compile:
"error: unbound placeholder parameter"
To fix it use:
<ul>{words.map(w => <li>{w}</li>)}</ul>
But if one needs more logic of some sort (e.g. if statements) a call for a method will do it.
Thanks Kristian
No worries, and sorry about the error - I was going from memory as it's been too long since I've done any Scala programming on a regular basis :-(
I'm not sure what you mean by "But if one needs more logic of some sort (e.g. if statements) a call for a method will do it". Do you mean, if there is more complicated logic inside the function passed to map, you would pull it out into its own function? If that's the case, then yes, I would agree.
This looks very nice - is there any syntactic sugar for parsing, rather than outputting, XML?
Oh yea, Scala is very strong in parsing XML, with nice use of pattern matching. See http://burak.emir.googlepages.com/scalaxbook.docbk.html
Thanks! Very helpful and exactly what I needed to build xml from a list.
Post a Comment