I was going to code up something quick to recall scala.xml and how much I dislike it; I haven't used it since I first learned some Scala.
You normally see text nodes of white space -- this is mentioned in PiS, in the "catalog" example here.
I did remember that it reverses attributes on load -- I vaguely remembered having to fix pretty printing.
But the compiler doesn't reverse attributes on xml literals. So given that you want to supply an xpath dynamically, you could use the compiler toolbox to compile the source document as a literal and also compile the xpath string, with / operators converted to \
.
That's just a little out-of-the-box fun, but maybe it has a sweet spot of applicability, perhaps if you must use only the standard Scala distro.
I'll update later when I get a chance to try it out.
import scala.xml._
import java.io.File
object Test extends App {
val src =
"""|<doc>
| <foo bar="red" baz="yellow"> <bar> red </bar> </foo>
| <baz><bar>red</bar></baz>
|</doc>""".stripMargin
val red = "(.*)red(.*)".r
val sub = "blue"
val tmp =
<doc>
<foo bar="red" baz="yellow"> <bar> red </bar> </foo>
<baz><bar>red</bar></baz>
</doc>
Console println tmp
// replace "red" with "blue" in all bar text
val root = XML loadString src
Console println root
val bars = root \\ "bar"
val barbars =
bars map (_ match {
case <bar>{Text(red(prefix, suffix))}</bar> =>
<bar>{Text(s"$prefix$sub$suffix")}</bar>
case b => b
})
val m = (bars zip barbars).toMap
val sb = serialize(root, m)
Console println sb
def serialize(x: Node, m: Map[Node, Node], sb: StringBuilder = new StringBuilder) = {
def serialize0(x: Node): Unit = x match {
case e0: Elem =>
val e = if (m contains e0) m(e0) else e0
sb append "<"
e nameToString sb
if (e.attributes ne null) e.attributes buildString sb
if (e.child.isEmpty) sb append "/>"
else {
sb append ">"
for (c <- e.child) serialize0(c)
sb append "</"
e nameToString sb
sb append ">"
}
case Text(t) => sb append t
}
serialize0(x)
sb
}
}