- There is no reference to the parent node because that would cause the XML to be very expensive during transformations
- Transforming the XML requires creating new nodes rather than changing the existing nodes
Both point cause non-functional programmers to feel a little uneasy but in practice only the first restriction causes any real discomfort.
Two methods for XML transformation will be demonstrated in this and the next topic.
- scala> val xml = <library>
- | <videos>
- | <video type="dvd">Seven</video>
- | <video type="blue-ray">The fifth element</video>
- | <video type="hardcover">Gardens of the moon</video>
- | </videos>
- | <books>
- | <book type="softcover">Memories of Ice</book>
- | </books>
- | </library>
- xml: scala.xml.Elem =
- <library>
- <videos>
- <video type="dvd">Seven</video>
- <video type="blue-ray">The fifth element</video>
- <video type="hardcover">Gardens of the moon</video>
- </videos>
- <books>
- <book type="softcover">Memories of Ice</book>
- </books>
- </library>
- scala> import scala.xml._
- import scala.xml._
- scala> import scala.xml.transform._
- import scala.xml.transform._
- // Some of the books are labelled as videos
- // not books so lets select those elements
- scala> val mislabelledBooks = xml \\ "video" filter {e => (e \\ "@type").text == "hardcover"}
- mislabelledBooks: scala.xml.NodeSeq = <video type="hardcover">Gardens of the moon</video>
- // we can create a rule that will remove all the
- // selected elements
- scala> object RemoveMislabelledBooks extends RewriteRule {
- | override def transform(n: Node): Seq[Node] ={
- | if (mislabelledBooks contains n) Array[Node]()
- | else n
- | }
- | }
- defined module RemoveMislabelledBooks
- // a quick test to make sure the elements are removed
- scala> new RuleTransformer(RemoveMislabelledBooks)(xml)
- res1: scala.xml.Node =
- <library>
- <videos>
- <video type="dvd">Seven</video>
- <video type="blue-ray">The fifth element</video>
-
- </videos>
- <books>
- <book type="softcover">Memories of Ice</book>
- </books>
- </library>
- // Now another rule to add them back
- scala> object AddToBooks extends RewriteRule {
- | override def transform(n: Node): Seq[Node] = n match {
- | case e:Elem if(e.label == "books") =>
- | val newBooks = mislabelledBooks map { case e:Elem => e.copy(label="book") }
- | e.copy(child = e.child ++ newBooks)
- | case _ => n
- | }
- | }
- defined module AddToBooks
- // voila done
- scala> new RuleTransformer(RemoveMislabelledBooks, AddToBooks)(xml)
- res4: scala.xml.Node =
- <library>
- <videos>
- <video type="dvd">Seven</video>
- <video type="blue-ray">The fifth element</video>
- </videos>
- <books>
- <book type="softcover">Memories of Ice</book>
- <book type="hardcover">Gardens of the moon</book></books>
- </library>
You missed an "import scala.xml.transform._". Also, I'd write one line inside AddToBooks in a different manner:
ReplyDeleteval newBooks = mislabelledBooks map { case mB: Elem => mB.copy(label="book") }
Thanks for both pointers. I have incorporated them into the post
ReplyDelete