How do I add an XML attribute, or not, depending on an Option?
I have written a makeMsg function but I don't like it - it just seems really un-Scala-ish to discriminate based on Option.isDefined. Can you make it bet开发者_开发问答ter?
scala> def makeMsg(t: Option[String]) =
| if (t.isDefined) <msg text={t.get} /> else <msg />
makeMsg: (t: Option[String])scala.xml.Elem
scala> makeMsg(Some("hello"))
res0: scala.xml.Elem = <msg text="hello"></msg>
scala> makeMsg(None)
res1: scala.xml.Elem = <msg></msg>
You can try this:
def makeMsg(t: Option[String]) = <msg text={t orNull} />
if attribute value is null
- it will not be added to the element.
Update
Even better! If you will add this implicit convertion:
import xml.Text
implicit def optStrToOptText(opt: Option[String]) = opt map Text
you can just use t
like this:
def makeMsg(t: Option[String]) = <msg text={t} />
Here is REPL session:
scala> import xml.Text
import xml.Text
scala> implicit def optStrToOptText(opt: Option[String]) = opt map Text
optStrToOptText: (opt: Option[String])Option[scala.xml.Text]
scala> def makeMsg(t: Option[String]) = <msg text={t} />
makeMsg: (t: Option[String])scala.xml.Elem
scala> makeMsg(Some("hello"))
res1: scala.xml.Elem = <msg text="hello"></msg>
scala> makeMsg(None)
res2: scala.xml.Elem = <msg ></msg>
This works because scala.xml.UnprefixedAttribute
has constructor that accepts Option[Seq[Node]]
as value.
What's wrong with this:
def makeMsg(t: Option[String]) = t match {
case Some(m) => <msg text={m} />
case None => <msg />
}
Not as concise as Easy Angel's, but it's straight up Scala.
Canonical Scala that does not require the text field to know to cleverly disappear when it's empty:
t.map(s => <msg text={s} />).getOrElse(<msg />)
You should think about using this pattern whenever you have an option but need to use something that doesn't know about options. (In this case, Easy Angel has found a more compact solution where it does know about options or something like them.)
精彩评论