這是。由於NamespaceBinding是嵌套的(每個ns都有一個父節點,TopScope除外),所以我們需要遞歸解決該問題。另外,每個ns都有一個URI和一個前綴,我們需要改變它們。
下面的函數只會改變一個特定的URI和前綴,它會檢查所有的名稱空間,看看是否需要改變前綴或URI。它會改變彼此獨立的前綴或URI,這可能不是想要的。不過,這並不是一件大事。
至於其餘的,只是在Elem模式匹配,以遞歸到XML的每個部分。啊,是的,它也改變了元素的前綴。再次,如果這不是我們想要的,那很容易改變。
該代碼假定不需要遞歸到XML的「其他」部分 - 其餘部分通常是文本元素。另外,它假設其他地方沒有命名空間。我不是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]
}