How to construct a NodeSeq programmatically?
I have a NodeSeq like this
val article_template =
<div>
<div class="title"></div>
<div class="content"></div>
<!-- some other markups don't know -->
</div>
I want to create this structure for each article in a List (defined as val articleList
), resulting in this:
<div>
<div class="title">title a</div>
<div class="content">content a</div>
<!-- some other markups don't know -->
</div>
<div>
<div class="title">title b</div>
<div class="content">content b</div>
<!-- some other markups don't know -->
</div>
...
Updated:
Please note the <!-- some other markups don't know -->
part,
so I cannot use XML literal syntax t开发者_JAVA百科o construct it.
I need a list of modified article_template
with corresponding article info.
See scala.xml.transform._ and the corresponding Stack Overflow questions.
It will probably look somewhat like this:
import scala.xml._
import transform._
case class Article( title: String, content: String )
val articleList = List(
Article("title 1","content 1"),
Article("title 2","content 2")
)
class TransformArticle(title: String, content: String) extends RewriteRule {
override def transform(n: Node): Seq[Node] = n match {
case div @ <div/> => div match {
case elem: Elem if elem \ "@class" contains Text("title") =>
elem copy (child = Text(title))
case elem: Elem if elem \ "@class" contains Text("content") =>
elem copy (child = Text(content))
case other => other
}
case other => other
}
}
val article_template =
<div>
<div class="title"></div>
<div class="content"></div>
<!-- some other markups don't know -->
</div>
Group(
articleList.flatMap( article => {
new RuleTransformer(new TransformArticle(article.title, article.content)) transform article_template
})
)
This is something lift is really good for
import net.liftweb.util.Helpers._
case class Article( title: String, content: String )
val articles = List(
Article("title a","content a"),
Article("title b","content b")
)
val snipet =
"article *" #> articles.map { article =>
"header h1 *" #> article.title &
".content *" #> article.content
}
val article_template =
<article>
<header>
<h1>Title</h1>
</header>
<section class="content">
Content
</section>
</article>
snipet( article_template )
Assuming that Article
is defined something like:
case class Article(title: String, content: String)
and articleList
as:
val articleList = List(
Article("title a","content a"),
Article("title b", "content b"))
You could produce the XML fragment with:
val xml: scala.xml.NodeSeq = articleList map { article =>
<div>
<div class="title">{article.title}</div>
<div class="content">{article.content}</div>
</div>
}
You can use Group to group XML elements. In XML literal it will look like this:
val xml =
<xml:group>
<div>
<div class="title">title a</div>
<div class="content">content a</div>
</div>
<div>
<div class="title">title b</div>
<div class="content">content b</div>
</div>
</xml:group>
Update
You can create XML dynamically like this:
case class Article(title: String, content: String)
val articles = List(Article("A1", "A1 content"), Article("A2", "A2 content"))
val xml =
<xml:group>{articles map { a =>
<div>
<div class="title">{a title}</div>
<div class="content">{a content}</div>
</div>
}}</xml:group>
Update 1
Here is another variation using template. Template is just a function that returns XML. In this case I using currying in order to produce list of templates for each article (that still needs some stuff to be provided):
case class Article(title: String, content: String)
val articles = List(Article("A1", "A1 content"), Article("A2", "A2 content"))
def articleTemplate[T](article: Article)(stuff: T) =
<div>
<div class="title">{article title}</div>
<div class="content">{article content}</div>
<div class="stuff">{stuff}</div>
</div>
val allArticles = articles map articleTemplate
val allArticlesWithStuff: NodeSeq =
allArticles.zipWithIndex map {case (f, i) => f(i)}
Use flatmap to add them together like this:
val xml: NodeSeq = List("Test", "Something", "Yo").flatMap(s => <a>{s}</a>)
- With articles instead of just strings of course
Update: Works well with matching as well
val xml: NodeSeq = List("Test", "Something", "Yo", 1).flatMap{
case s: String => <string>{s}</string>
case i: Int => <int>{i}</int>
}
精彩评论