Transforming an XML element based on its sibling, using scala.xml.transform.RuleTransformer

Given this XML:

<root>
 <item>
   <discriminator>d1</discriminator>
   <target>t1</target>
   <subitem>
       <incomplete></incomplete>
   </subitem>
   <subitem>
       <incomplete></incomplete>
   </subitem>
 </item>
 <item>
    <discriminator>d2</discriminator>
   <target>t2</target>
   <subitem>
       <incomplete></incomplete>
   </subitem>
 </item>
</root>

I need to transform it such that: 1) for each <item>, text of <target> is modified based on the text in <discriminator>. 2) for each <incomplete>, add some text content.

Based on other SO posts, so far I've come up with a solution for 2), using RewriteRule and RuleTransformer. It goes like this:

object completeIncomplete extends RewriteRule {
      override def transform(n: Node): Seq[Node] = n match {
        case Elem(_, "incomplete", _, _, _*) =>
          <incomplete>content</incomplete>
        case other => other
      }
    }
object transform extends RuleTransformer(completeIncomplete)
transform(xml)

This seems to work fine, but I don't know:

  • how to achieve 1) (modifying an element based on its sibling)
  • how to combine the two processing steps. That is: does it make sense in this case to have two different RewriteRule-s? Is that two wasteful? Is it possible/advisable to achieve both transformations in a single iteration?

For 1), I have tried to pattern match the list of children, like this:

case Elem("", "item", _, _, disc@Elem("", "discriminator", _, _, _*)) 
    if discriminate(disc) => //...?

or this:

case item@Elem("", "item", _, _, _*) 
    if discriminate(item \ "disc") => //..?

But that didn't work out so well, because I don't know how to recreate the whole item with only replacing <target>

I don't even know if in this case matching against <item> is the way to go. But if it is, can I somehow achieve the transformation of its <incomplete>children?

What is the correct approach here?

Answers


See if this works for you:

  override def transform(n: Node): Seq[Node] = n match {

    case el @ Elem(_, "item", _, _, _*) =>
      val children = el.child
      val disc = children.find(_.label == "discriminator")
      val content = children.collect{
        case el @ Elem(_, "target", _, _, _*) =>
          <target>{el.text + disc.map(_.text).getOrElse("")}</target>
        case e:Elem => e
      }
      <item>{content}</item>

    case el @ Elem(_, "incomplete", _, _, _*) =>  
      <incomplete>some content</incomplete>
    case other => other
  }

The xml structure is immutable, so when you are on a particular element, you can not access it's parent. Because of that, I chose to approach your problem #1 by stopping on item instead and replacing its child content. I hope this is what you were looking for.


Need Your Help

Bidirectional sub/resource relationships in Angular ui-router

javascript angularjs angular-ui-router

So there's this nifty thing I can do with my resource classes in Jax-RS:

Custom upload field in wordpress user profile

php wordpress file-upload

I'm trying to allow users to upload a profile image on their user profile editing page, here's the code for the upload form:

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.