Вот оно. Поскольку 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]
}