我正在使用scala通过scala.xml.XML.loadFile()
方法从文件加载XML文件。我正在使用的文档已经定义了名称空间,我希望使用scala将名称空间更改为其他名称空间。例如,文档的xmlns为“http://foo.com/a”,前缀为“a” - 我想将文档的名称空间和前缀分别更改为“http://foo.com/b”和“b”。
看似简单,我觉得我在这里遗漏了一些明显的东西。从引用的Elem
方法返回loadFile()
中获取命名空间时没有问题。
答案 0 :(得分:9)
在这里。由于NamespaceBinding是嵌套的(每个ns都有一个父,但TopScope除外),我们需要递归来修复它。此外,每个ns都有一个URI和一个前缀,我们需要更改它们。
下面的函数只会更改一个特定的URI和前缀,它会检查所有名称空间,以查看前缀或URI是否需要更改。它将改变彼此独立的前缀或URI,这可能不是所需的。不过,这并不是什么大不了的事。
至于其余部分,只需在Elem上进行模式匹配即可递归到XML的每个部分。啊,是的,它也改变了元素的前缀。再说一次,如果那不是想要的,那就很容易改变。
代码假设不需要递归到XML的“其他”部分 - 其余部分通常是Text元素。此外,它假设其他地方没有命名空间。我不是XML方面的专家,所以我两个方面都错了。再一次,应该很容易改变它 - 只需遵循模式。
def changeNS(el: Elem,
oldURI: String, newURI: String,
oldPrefix: String, newPrefix: String): Elem = {
def replace(what: String, before: String, after: String): String =
if (what == before) after else what
def fixScope(ns: NamespaceBinding): NamespaceBinding =
if(ns == TopScope)
TopScope
else new NamespaceBinding(replace(ns.prefix, oldPrefix, newPrefix),
replace(ns.uri, oldURI, newURI),
fixScope(ns.parent))
def fixSeq(ns: Seq[Node]): Seq[Node] = for(node <- ns) yield node match {
case Elem(prefix, label, attribs, scope, children @ _*) =>
Elem(replace(prefix, oldPrefix, newPrefix),
label,
attribs,
fixScope(scope),
fixSeq(children) : _*)
case other => other
}
fixSeq(el.theSeq)(0).asInstanceOf[Elem]
}
但这会产生意想不到的结果。范围正在添加到所有元素。那是因为NamespaceBinding没有定义equals方法,因此使用引用相等。我已经为它开了一张票,2138已经关闭了,所以Scala 2.8不会出现这个问题。
同时,以下代码将正常运行。它保留了名称空间的缓存。它还会在处理之前将NamespaceBinding分解为一个列表。
def changeNS(el: Elem,
oldURI: String, newURI: String,
oldPrefix: String, newPrefix: String): Elem = {
val namespaces = scala.collection.mutable.Map.empty[List[(String, String)],NamespaceBinding]
def replace(what: String, before: String, after: String): String =
if (what == before) after else what
def unfoldNS(ns: NamespaceBinding): List[(String, String)] = ns match {
case TopScope => Nil
case _ => (ns.prefix, ns.uri) :: unfoldNS(ns.parent)
}
def foldNS(unfoldedNS: List[(String, String)]): NamespaceBinding = unfoldedNS match {
case knownNS if namespaces.isDefinedAt(knownNS) => namespaces(knownNS)
case (prefix, uri) :: tail =>
val newNS = new NamespaceBinding(prefix, uri, foldNS(tail))
namespaces(unfoldedNS) = newNS
newNS
case Nil => TopScope
}
def fixScope(ns: NamespaceBinding): NamespaceBinding =
if(ns == TopScope)
ns
else {
val unfoldedNS = unfoldNS(ns)
val fixedNS = for((prefix, uri) <- unfoldedNS)
yield (replace(prefix, oldPrefix, newPrefix), replace(uri, oldURI, newURI))
if(!namespaces.isDefinedAt(unfoldedNS))
namespaces(unfoldedNS) = ns // Save for future use
if(fixedNS == unfoldedNS)
ns
else
foldNS(fixedNS)
}
def fixSeq(ns: Seq[Node]): Seq[Node] = for(node <- ns) yield node match {
case Elem(prefix, label, attribs, scope, children @ _*) =>
Elem(replace(prefix, oldPrefix, newPrefix),
label,
attribs,
fixScope(scope),
fixSeq(children) : _*)
case other => other
}
fixSeq(el.theSeq)(0).asInstanceOf[Elem]
}
答案 1 :(得分:0)
这里的小错误。属性也可以具有限定名称。你也需要检查它们。