drbv
drbv

Reputation: 369

How can I transform only the top level nodes of an xml file in scala (not the descendants)

I have some xml where there is an <id/> node at the top level, and also inside descendant nodes. Is there a way for me to change the value at the top level and not at the lower levels?

Sample input is

val inputXml = 
  <top>
    <id>123</id>
    <a>
      <id>innerId</id>
    </a>
    <b>other info</b>
    <c>
      <id>444</id>
    </c>
  </top>

The output I'd like to see is:

val expectedOutputXml =
  <top>
    <id>999</id>
    <a>
      <id>innerId</id>
    </a>
    <b>other info</b>
    <c>
      <id>444</id>
    </c>
  </top>

I've tried using RewriteRule like this:

import scala.xml.transform.{RewriteRule, RuleTransformer}
import scala.xml.{Elem, Node, NodeSeq}

def changeTopId(root:Node, newId:String) = {

  val dontChg = root \ "_" filter {e => List("a", "b").contains(e.label)}

  object t1 extends RewriteRule{
    override def transform(n: Seq[Node]): Seq[Node] = n match {
      case e:Elem if dontChg.contains(e) => e
      case e:Elem if e.label == "id" => <id>{newId}</id>
      case other => other
    }
  }
  object changeTopOne extends RuleTransformer(t1)

  changeTopOne(root)
}

But it affects all <id/> nodes, resulting in:

scala> changeTopId(inputXml, "999")
res1: scala.xml.Node =
<top>
    <id>999</id>
    <a>
      <id>999</id>
    </a>
    <b>other info</b>
    <c>
      <id>999</id>
    </c>
  </top>

Thanks

Upvotes: 1

Views: 186

Answers (1)

drbv
drbv

Reputation: 369

Turns out I didn't need Rewrite Rule. Here's a 5 line solution.

val xs = (inputXml \ "_") map {
  case e: Elem if e.label == "id" => <id>999</id>
  case other => other
}

inputXml.copy(child=xs)

And the resulting value (formatted manually)

scala.xml.Elem =
<top>
    <id>999</id>
    <a>
        <id>innerId</id>
    </a>
    <b>other info</b>
    <c>
        <id>444</id>
    </c>
</top>

Upvotes: 1

Related Questions