I am using the Scala play framework. For most of my pages I keep my HTML strictly within the view templates. For one page in particular though, I would like to be able pass through some XML I have generated programatically in my controller. I have the following trivial template:
@(session:play.mvc.Scope.Session, flash:play.mvc.Scope.Flash, analysisTable : scala.xml.Node )
@main(title="Home", session=session, flash=flash) {
<h1>Some title</h1>
@{analysisTable}
}
When I pass through some pre-generated XML that I am hoping to embed and navigate to the appropriate page, I get:
Execution exception
InvocationTargetException occured : null
On the page, and what looks like a stack overflow on the console (excerpt below):
at play.templates.BaseScalaTemplate._display_(ScalaTemplate.scala:618)
at play.templates.BaseScalaTemplate$$anonfun$_display_$1.apply(ScalaTemplate.scala:618)
at play.templates.BaseScalaTemplate$$anonfun$_display_$1.apply(ScalaTemplate.scala:618)
at scala.xml.NodeSeq.foreach(NodeSeq.scala:43)
at scala.xml.NodeSeq.foldLeft(NodeSeq.scala:43)
at play.templates.BaseScalaTemplate._display_(ScalaTemplate.scala:618)
at play.templates.BaseScalaTemplate$$anonfun$_display_$1.apply(ScalaTemplate.scala:618)
at play.templates.BaseScalaTemplate$$anonfun$_display_$1.apply(ScalaTemplate.scala:618)
at scala.xml.NodeSeq.foreach(NodeSeq.scala:43)
at scala.xml.NodeSeq.foldLeft(NodeSeq.scala:43)
at play.templates.BaseScalaTemplate._display_(ScalaTemplate.scala:618)
at play.templates.BaseScalaTemplate$$anonfun$_display_$1.apply(ScalaTemplate.scala:618)
at play.templates.BaseScalaTemplate$$anonfun$_display_$1.apply(ScalaTemplate.scala:618)
at scala.xml.NodeSeq.foreach(NodeSeq.scala:43)
at scala.xml.NodeSeq.foldLeft(NodeSeq.scala:43)开发者_如何学运维
at play.templates.BaseScalaTemplate._display_(ScalaTemplate.scala:618)
at play.templates.BaseScalaTemplate$$anonfun$_display_$1.apply(ScalaTemplate.scala:618)
at play.templates.BaseScalaTemplate$$anonfun$_display_$1.apply(ScalaTemplate.scala:618)
at scala.xml.NodeSeq.foreach(NodeSeq.scala:43)
at scala.xml.NodeSeq.foldLeft(NodeSeq.scala:43)
Clearly I am doing something either dumb or not allowed. Would anyone care to put me out of my misery?
OK. I read the sources and worked out a fix. The scala play templating engine seems to allow a defined set of different types within the template for internal rendering (exceprt from play-scala / src / play / templates / ScalaTemplate.scala), not including scala.xml.Node:
case class BaseScalaTemplate[T<:Appendable[T],F<:Format[T]](format: F) {
def _display_(o:Any):T = {
o match {
case escaped:T => escaped
case () => format.raw("")
case None => format.raw("")
case Some(v) => _display_(v)
case escapeds:Seq[Any] => escapeds.foldLeft(format.raw(""))(_ + _display_(_))
case string:String => format.escape(string)
case v if v != null => _display_(v.toString)
case _ => format.raw("")
}
}
}
Mostly one puts in strings, but there are a few other types accepted. Looking at the top line of the match statement above, anything that is a subtype of Appendable is allowed. And it just so happens that a little further up there is the following class defined:
case class Html(text:String) extends Appendable[Html] {
val buffer = new StringBuilder(text)
def +(other:Html) = {
buffer.append(other.buffer)
this
}
override def toString = buffer.toString
}
My solution then, is to convert the original xml subtree to a string and then wrap that in the Html class as follows:
@(session:play.mvc.Scope.Session, flash:play.mvc.Scope.Flash, analysisTable : scala.xml.Node )
@main(title="Home", session=session, flash=flash) {
<h1>Some title</h1>
@{new play.templates.Html(analysisTable.toString)}
}
精彩评论