I have one Map which contains some attributes, and I'd like to create a xml element which has these attributes.
e.g. config is scala Map, and I want to use it as following, but actually the following doesn't work. Could anyone help me ? Thanks
<input type="text" {config.map(entry => entry._1 + "=" + entry._2)}></input>
Use the low-level API. This is what's generated by scalac parser when desugaring your xml literals:
import scala.xml.{UnprefixedAttribute, MetaData, TopScope, Text, Elem, Null}
val config: Map[String, String] = Map("k1" -> "v2", "k2" -> "v2")
val seed: MetaData = Null // Don't ask.
val meta: MetaData = config.toList.foldLeft(seed) {
case (acc, (s1, s2)) =>
new UnprefixedAttribute(
key = s1,
value = s2,
next = acc
)
}
Elem(
prefix = null,
label = "input",
attributes = meta,
scope = TopScope,
minimizeEmpty = false
)
Bummer you can't write what you wanted. I guess the Scala compiler's XML literal parser isn't sophisticated enough to parse XML literals with expressions just inserted anywhere. They can only be between tags or as individual attribute values.
As a workaround, it is possible to create the element with no attributes, or just the one attribute and the rest missing:
val elem = <input type="text"/>
Then you can add the attributes from your Map
with a couple of copy constructors.
elem.copy(
attributes = elem.attributes.copy(
config.foldLeft(scala.xml.Null: scala.xml.MetaData) {
case (next, (k, v)) => scala.xml.Attribute(k, scala.xml.Text(v), next)
}
))
res1: scala.xml.Elem = <input type="text" k2="v2" k1="v2"/>
The above uses the copy constructor for the Elem
and then one for the attributes
.
There's also the option of just using one copy constructor and folding a little differently:
elem.copy(attributes = config.foldRight(elem.attributes) {
case ((k, v), next) => scala.xml.Attribute(k, scala.xml.Text(v), next)
})
res2: scala.xml.Elem = <input k1="v2" k2="v2" type="text"/>
That might be the simplest option.
Although, Olivier's solution does show that UnprefixedAttributed
will accept a String
for an attribute value.
elem.copy(attributes = config.foldRight(elem.attributes) {
case ((k, v), next) => new scala.xml.UnprefixedAttribute(k, v, next)
})
res3: scala.xml.Elem = <input k1="v2" k2="v2" type="text"/>
That avoids having to make a Text
element so that you provide a NodeSeq
value for Attribute
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With