如何更改 Scala XML 元素上的属性 [英] How to change attribute on Scala XML Element
问题描述
我有一个 XML 文件,我想用脚本映射其中的一些属性.例如:
I have an XML file that I would like to map some attributes of in with a script. For example:
<a>
<b attr1 = "100" attr2 = "50"/>
</a>
可能具有按 2 倍缩放的属性:
might have attributes scaled by a factor of two:
<a>
<b attr1 = "200" attr2 = "100"/>
</a>
这个页面有一个关于添加属性的建议,但没有详细说明用函数映射当前属性的方法(这种方式会变得非常困难):http://www.scalaclass.com/book/export/html/1
This page has a suggestion for adding attributes but doesn't detail a way to map a current attribute with a function (this way would make that very hard): http://www.scalaclass.com/book/export/html/1
我想出的是手动创建 XML(非 scala)链接列表......类似于:
What I've come up with is to manually create the XML (non-scala) linked-list... something like:
// a typical match case for running thru XML elements:
case Elem(prefix, e, attributes, scope, children @ _*) => {
var newAttribs = attributes
for(attr <- newAttribs) attr.key match {
case "attr1" => newAttribs = attribs.append(new UnprefixedAttribute("attr1", (attr.value.head.text.toFloat * 2.0f).toString, attr.next))
case "attr2" => newAttribs = attribs.append(new UnprefixedAttribute("attr2", (attr.value.head.text.toFloat * 2.0f).toString, attr.next))
case _ =>
}
Elem(prefix, e, newAttribs, scope, updateSubNode(children) : _*) // set new attribs and process the child elements
}
它可怕、冗长且不必要地重新排序输出中的属性,由于一些糟糕的客户端代码,这对我当前的项目不利.有没有一种 Scala 式的方法来做到这一点?
Its hideous, wordy, and needlessly re-orders the attributes in the output, which is bad for my current project due to some bad client code. Is there a scala-esque way to do this?
推荐答案
好的,尽力而为,Scala 2.8.我们需要重建属性,这意味着我们必须正确分解它们.让我们为此创建一个函数:
Ok, best effort, Scala 2.8. We need to reconstruct attributes, which means we have to decompose them correctly. Let's create a function for that:
import scala.xml._
case class GenAttr(pre: Option[String],
key: String,
value: Seq[Node],
next: MetaData) {
def toMetaData = Attribute(pre, key, value, next)
}
def decomposeMetaData(m: MetaData): Option[GenAttr] = m match {
case Null => None
case PrefixedAttribute(pre, key, value, next) =>
Some(GenAttr(Some(pre), key, value, next))
case UnprefixedAttribute(key, value, next) =>
Some(GenAttr(None, key, value, next))
}
接下来,让我们将链接的属性分解为一个序列:
Next, let's decompose the chained attributes into a sequence:
def unchainMetaData(m: MetaData): Iterable[GenAttr] =
m flatMap (decomposeMetaData)
此时,我们可以轻松操作这个列表:
At this point, we can easily manipulate this list:
def doubleValues(l: Iterable[GenAttr]) = l map {
case g @ GenAttr(_, _, Text(v), _) if v matches "\\d+" =>
g.copy(value = Text(v.toInt * 2 toString))
case other => other
}
现在,再次链接它:
def chainMetaData(l: Iterable[GenAttr]): MetaData = l match {
case Nil => Null
case head :: tail => head.copy(next = chainMetaData(tail)).toMetaData
}
现在,我们只需要创建一个函数来处理这些事情:
Now, we only have to create a function to take care of these things:
def mapMetaData(m: MetaData)(f: GenAttr => GenAttr): MetaData =
chainMetaData(unchainMetaData(m).map(f))
所以我们可以这样使用它:
So we can use it like this:
import scala.xml.transform._
val attribs = Set("attr1", "attr2")
val rr = new RewriteRule {
override def transform(n: Node): Seq[Node] = (n match {
case e: Elem =>
e.copy(attributes = mapMetaData(e.attributes) {
case g @ GenAttr(_, key, Text(v), _) if attribs contains key =>
g.copy(value = Text(v.toInt * 2 toString))
case other => other
})
case other => other
}).toSeq
}
val rt = new RuleTransformer(rr)
最终让你做你想要的翻译:
Which finally let you do the translation you wanted:
rt.transform(<a><b attr1="100" attr2="50"></b></a>)
如果满足以下条件,所有这些都可以简化:
All of this could be simplified if:
- 属性实际定义了前缀、键和值,带有可选的前缀
- 属性是一个序列,而不是一个链
- 属性有一个地图、mapKeys、mapValues
- Elem 有一个 mapAttribute
这篇关于如何更改 Scala XML 元素上的属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!