- 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