Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Processing optional xml attributes in Scala

I have code that reads an XML file. Some of the attributes of elements that I need to process are optional. I am trying to use Option[T] to manage them. I have written the following to pimp the NodeSeq type returned by the \ Node operator:

class NodeSeqWrapper(nodeSeq: NodeSeq) {
  def textOption: Option[String] = {
    val text = nodeSeq.text
    if (text == null || text.length == 0) None else Some(text)
  }
}
implicit def nodeSeqWrapper(nodeSeq: NodeSeq): NodeSeqWrapper =
  new NodeSeqWrapper(nodeSeq)

and then call it like this:

(node \ "@attr").textOption.getOrElse("Some default value")

If the node has the "attr" attribute, this code gets it value. If it does not, the value "Some default value" is returned.

How can I improve this? Is there some way to fold the class definition into the implicit method? Is there a better way of getting "optional" attribute values? Am I using Option[T] "correctly"?

like image 563
Ralph Avatar asked Oct 12 '11 12:10

Ralph


2 Answers

I would say you are doing it in a very idiomatic way, yes.

You can "fold the definitions" as follows:

implicit def enrichNodeSeq(nodeSeq: NodeSeq) = new AnyRef {
  def textOption : Option[String] = {
    val text = nodeSeq.text
    if (text == null || text.length == 0) None else Some(text)
  }
}

If you are always applying .getOrElse(...) on the result, you may also want to define a second version textOrElse(elze : String) : String:

implicit def enrichNodeSeq(nodeSeq: NodeSeq) = new AnyRef {
  def textOption : Option[String] = {
    val text = nodeSeq.text
    if (text == null || text.length == 0) None else Some(text)
  }

  def textOrElse(elze : String) : String = textOption.getOrElse(elze)
}

That will make things slightly more concise.

scala> (<b>Hello</b> : NodeSeq).textOrElse("No text found.")
resN: String = Hello
scala> (<br /> : NodeSeq).textOrElse("No text found.")
resM: String = No text found.
like image 197
Philippe Avatar answered Nov 06 '22 06:11

Philippe


The answer can be improved on since Scala 2.10 with the introduction of implicit classes.

http://docs.scala-lang.org/overviews/core/implicit-classes.html

The example the op gave can be re-written using an implicit class like so:

object SomeExtensions {

  implicit class ExtendedNodeSeq(nodeSeq: NodeSeq) {
    def textOption: Option[String] = {
      val text = nodeSeq.text
      if (text == null || text.length == 0) None else Some(text)
    }
  }

}

Note that the example follows a couple of the restrictions for case classes:

  1. They must be defined inside of another trait/class/object.
  2. They may only take one non-implicit argument in their constructor.
like image 41
be.outside Avatar answered Nov 06 '22 07:11

be.outside