Search code examples
scalaxml-parsingscala-option

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"?


Solution

  • 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.