Scala - Modify value of arbitrary XML element

1.1k Views Asked by At

I want to choose arbitrary element in xml at runtime and update its value. For example, given xml like

<root>
  <abc>123</abc>
  <def>456</def>
</root>

I want to arbitrary pick either <abc> or <def> at runtime (not hardcoded) and update its value. I found many question in related links but all solution hard code label name upfront.

I tried function that takes element name and new value as params like

object RuleFactory {

  // This function should not hard code literals "abc" or "def"
  def createRuleTransformer(name:String, value : String) : RuleTransformer = {
    new RuleTransformer(new RewriteRule {
      override def transform(n: Node): Seq[Node] = n match {
        case elem @ Elem(prefix, label, attribs, scope, _) if elem.label.equalsIgnoreCase(name) => Elem(prefix, label, attribs, scope, false, Text(value))
        case other => other
      }
    })
  }

}

and

object RuleFactory {
  def createRuleTransformer(name:String, value : String) : RuleTransformer = {
    new RuleTransformer(new RewriteRule {
      override def transform(n: Node): Seq[Node] = n match {
        case elem : Elem if elem.label.equalsIgnoreCase(name) => elem copy (child = Text(value) flatMap (this transform))
        case other => other
      }
    })
  }
}

However both don't print updated xml when I perform transform like

val trasnformer = RuleFactory.createRuleTransformer("def", "2") // These params will be random
println(trasnformer(InputXml))

Is this possible with RewriteRule?

1

There are 1 best solutions below

0
On BEST ANSWER

From https://github.com/scala/scala-xml/issues/129

It turns out to be unfortunate name-collision because of the lexical scoping rules of Scala. It's a consequence of this class member:

http://www.scala-lang.org/api/current/scala-xml/scala/xml/transform/RewriteRule.html#name:String

https://github.com/scala/scala-xml/blob/4c09977/src/main/scala/scala/xml/transform/RewriteRule.scala#L23

Changing variable name solve the problem. Here is updated code.

object RuleFactory {

  def createRuleTransformer(key:String, value : String) : RuleTransformer = {
    new RuleTransformer(new RewriteRule {
      override def transform(n: Node): Seq[Node] = n match {
        case elem @ Elem(prefix, label, attribs, scope, _) if elem.label.equalsIgnoreCase(key) =>
          Elem(prefix, label, attribs, scope, false, Text(value))
        case other => other
      }
    })
  }

}