By Alvin Alexander. Last updated: July 23, 2016
Summary: This is an example of how to use XML and XPath in Scala.
I'm not going to take any time to describe the following Scala XML/XPath example, other than to say that when it's run, it produces the following output, which is a simulated receipt for an order at a pizza store:
14 thin (Cheese, Sausage) 12.00 Breadsticks 4.00 Federal Tax 0.80 State Tax 0.80 Local Tax 0.40 TOTAL: 18.00
The XML
The XML that represents the "order" is shown at the bottom of the code, but I'll repeat it here for your convenience:
def getXml = { <order> <item name="Pizza" price="12.00"> <pizza> <crust type="thin" size="14" /> <topping>cheese</topping> <topping>sausage</topping> </pizza> </item> <item name="Breadsticks" price="4.00"> <breadsticks /> </item> <tax type="federal">0.80</tax> <tax type="state">0.80</tax> <tax type="local">0.40</tax> </order> }
The Scala XML XPath code
Given that extremely limited introduction, here is today's Scala XML and XPath example code:
package com.devdaily.xmltests import scala.xml._ /** * A Scala XML XPath example. * Author: Alvin Alexander, http://devdaily.com */ object ScalaXmlXpathExample extends App { println(getReceipt(getXml)) def getReceipt(xml: Elem): String = { val sb = new StringBuilder sb.append(getLineItemsReceiptString(xml)) sb.append(getTaxItemsReceiptString(xml)) sb.append(getTotalLineForReceipt(xml)) sb.toString } def getLineItemsReceiptString(xml: Elem) = { buildReceiptString(getOrderItems(xml), getOrderItemNameAndPrice) } def getTaxItemsReceiptString(xml: Elem) = { buildReceiptString(getTaxItems(xml), getTaxItemNameAndAmount) } def getTotalLineForReceipt(xml: Elem) = { "%-30s %6.2f\n".format("TOTAL:", getTotalPrice(getOrderItems(xml), getTaxItems(xml))) } def getOrderItems(rootNode: Elem) = rootNode \ "item" def getTaxItems(rootNode: Elem) = rootNode \ "tax" // can reuse the other methods, or make these calls. // specifying Double as the return type here is needed, otherwise Scala // will make it a BoxedUnit. def getTotalPrice(items: NodeSeq, taxItems: NodeSeq): Double = { val priceWithoutTax = items.map(i => (i \ "@price").text.toDouble).sum val totalTax = taxItems.map(i => i.text.toDouble).sum priceWithoutTax + totalTax } // items can be orderItems or taxItems; the function 'f' should // correspond to that data type (getOrderItemNameAndPrice or getTaxItemNameAndAmount). def buildReceiptString(items: NodeSeq, f:(Node) => (String, Double)) = { val sb = new StringBuilder items.foreach{ i => val (name, value) = f(i) sb.append("%-30s %6.2f\n".format(name, value)) } sb.toString } def getOrderItemNameAndPrice(itemNode: Node) = { val name = getDisplayableOrderItemName(itemNode) val price = (itemNode \ "@price").text.toDouble (name, price) } def getDisplayableOrderItemName(itemNode: Node): String = { getItemName(itemNode) match { case "Pizza" => getPizzaNameForReceipt(itemNode) case "Breadsticks" => getBreadsticksNameForReceipt(itemNode) case _ => "other" } } def getTaxItemNameAndAmount(taxNode: Node) = { val name = getDisplayableTaxItemName(taxNode) val amount = taxNode.text.toDouble (name, amount) } def getDisplayableTaxItemName(taxNode: Node) = { getTaxItemName(taxNode) match { case "local" => "Local Tax" case "state" => "State Tax" case "federal" => "Federal Tax" case _ => "Unknown Tax" } } def getTaxItemName(taxNode: Node) = (taxNode \ "@type").text def getItemName(itemNode: Node) = (itemNode \ "@name").text def getBreadsticksNameForReceipt(item: Node) = "Breadsticks" // want a name like: 12" Thin (Cheese, Sausage) def getPizzaNameForReceipt(itemNode: Node) = { val pizzaNode = (itemNode \ "pizza")(0) val crust = getPizzaCrust(pizzaNode) val size = getPizzaSize(pizzaNode) "%2d %s %s".format(size, crust, getPizzaToppingsForReceipt(pizzaNode)) } def getPizzaToppingsForReceipt(pizzaNode: Node) = { getToppings(pizzaNode).map(_.capitalize).mkString("(", ", ", ")") } def getToppings(pizzaNode: Node) = (pizzaNode \ "topping").map(_.text) def getPizzaCrust(pizzaNode: Node) = (pizzaNode \ "crust" \ "@type").text def getPizzaSize(pizzaNode: Node) = (pizzaNode \ "crust" \ "@size").text.toInt def getXml = { <order> <item name="Pizza" price="12.00"> <pizza> <crust type="thin" size="14" /> <topping>cheese</topping> <topping>sausage</topping> </pizza> </item> <item name="Breadsticks" price="4.00"> <breadsticks /> </item> <tax type="federal">0.80</tax> <tax type="state">0.80</tax> <tax type="local">0.40</tax> </order> } }
Directly or indirectly this code uses the following Scala XML classes:
If you're using XML and XPath in Scala, I hope this example is helpful. Leave a note in the Comments section below with any questions or improvements.