We recently wanted to do some transformation on our small XML document in order to present some information on it. In this example, our example looks like this:

<basket>
  <item cost=".99" discountCode="A5">Last year's red Christmas baubles</item>
  <item cost=".10" discountCode="A5">Halloween lamps</item>
  <item cost="101.99" >Pine Tree</item>
  <item cost="20" discountCode="B2">Gold star</item>
</basket>

It’s a pretty simple structure with some simple rules. A basket can contain one or more items. Each item has:

  • A simple description
  • A cost in dollars
  • An optional discount code

The task we had was to display a list of human-readable discounts that had been applied to this particular basket. In the example given about the discount codes are A5 and B2.

We have a very simple list of known discounts, and can implement a simple conversion function:

object Discounts {
  def displayString(code : String): String = {
    code match  {
      case "A5" => "Reduced to clear"
      case "B2" => "Pre-Christmas offer"
    }
  }
}

We already had a class that wrapped this small XML document, creating a cached field for the item nodes.

class Basket(val rootNode: Node) {
  private lazy val items = (rootNode \ "item");
}

We can then apply a series of functions to convert the items:

class Basket(val rootNode: Node) {
  private lazy val items = (rootNode \ "item");

  lazy val discountsApplied = 
    items.map(item => item.attribute("discountCode")
         .map(attribute => displayString(attribute.text)))
         .flatten
}

The above code pulls out all the “discountCode” attributes, and applies a conversion function to convert them to human-readable form. Since item.attribute returns an Option, we use flatten to get rid of elements that are essentially null.

Unfortunately, while there is a toSet method, and a list, there isn’t a great way to do both inbuilt. However, by constructing a TreeSet (a sorted set), we can append all the results and get the same effect. We end up with the following:

class Basket(val rootNode: Node) {
  private lazy val items = (rootNode \ "item");

  lazy val discountsApplied = sortedUniqueSet(
    items.map(item => item.attribute("discountCode")
         .map(attribute => displayString(attribute.text)))
         .flatten)

  private def sortedUniqueSet(sequence: Seq[String]) = {
    TreeSet.empty[String] ++ sequence
  }
}