Xml och XPath i Scala

Scala inkluderar direkt stöd för Xml som en egen datatyp.

Konkret betyder detta bl.a. att man kan skriva följande scala-kod:

	val someXml = <a><b myattribute="foo">Hello</b></a>
	println(someXml.getClass.getName) // resulterande output: "scala.xml.Elem"
	println(someXml \ "b" \ "@myattribute") // resulterande output: "foo"

Det sistnämnda uttrycket ovan påminner om XPath. Med XPath hämtar man nämligen ett attribut-värde med "@" och använder en "framåt-slash" för att navigera ner i underliggande element, medan Scala-koden ovan använder en "bakåt-slash" i stället.

Bakåt-slashen ovan är faktiskt ett metod-anrop på en instans av klassen scala.xml.Elem (men metoden ärvs av klassen scala.xml.NodeSeq)

Metod-anropet ovan kan därför skrivas på ett alternativt sätt med punkt-notation mellan objektet och parenteser runt parametern, dvs på liknande sätt som man brukar göra i många andra programmeringsspråk inklusive Java, dvs så här:

	val someXml = <a><b myattribute="foo">Hello</b></a>
	println(someXml.\("b").\("@myattribute")) // resulterande output: "foo"

När det gäller stödet för XPath i Scala så är det dock inte särskilt omfattande (per Scala version 2.7.7 som 2010-02-12 då detta skrivs är den senaste stabila versionen) men det är inget stort problem eftersom det går att använda Java-bibliotek om man har behov av att använda XPath-frågor.

På websidan 'Xml and XPath in Scala' finns källkod (och kommentarer på engelska) för ett exempel som visar hur man från Scala-kod kan använda vanliga Java-klasser och interface.

Dessutom visas hur man kan göra det m.h.a. så kallade "implicit conversions" som kan göra så att klientkoden ser ut som om klassen 'scala.xml.Elem' innehåller en metod som faktiskt inte finns.

Den ovan länkade sidan illustrerar bl.a. (enligt kodsnutten nedan) att du kan anropa metoden 'getNodeListMatchingXPathExpression' på en instans av klassen 'scala.xml.Elem' trots att det egentligen inte finns någon sådan metod utan det är en metod som man själv kan skapa på en annan klass.

Genom en implicit konvertering av 'scala.xml.Elem' till den egna klassen så kommer dock nedanstående kod att fungera. Dessutom kan det poängteras att Scala är statiskt typat, dvs kompilatorn kommer att upptäcka ifall det kommer att fungera eller inte.

	val xmlWithBooks: scala.xml.Elem = ...
	val nodeList: org.w3c.dom.NodeList = xmlWithBooks.getNodeListMatchingXPathExpression("/bookstore/book[1]/title")

Med ett språk som Java, utan möjligheter till extension methods eller implicit konvertering så skulle ovanstående kod vara nödvändig att implementera med någon typ av utility klass, ungefär så här:

	val xmlWithBooks: scala.xml.Elem = ...
	val nodeList: org.w3c.dom.NodeList = XmlFacade.getNodeListMatchingXPathExpression(xmlWithBooks, "/bookstore/book[1]/title")

Kodexemplet använder sig alltså bl.a. av befintliga Java-bibliotek, och om du vill se fler exempel på samarbete mellan Java-kod och Scala-kod så kan du även titta på sidan 'Interoperabilitet mellan Scala och Java och tredjepartsbibliotek'.

Om du vill se fler exempel på "implicita konverteringar" så kan du även titta på sidan om 'objektorienterad Scala'.

/ Tomas Johansson, 2010-02-12