Đây rồi. Vì NamespaceBinding được lồng nhau (mỗi ns có một parent, ngoại trừ TopScope), chúng ta cần phải recurse để sửa lỗi đó. Ngoài ra, mỗi ns có một URI và một tiền tố, và chúng ta cần phải thay đổi cả hai.
Hàm bên dưới sẽ chỉ thay đổi một URI và tiền tố cụ thể và nó sẽ kiểm tra tất cả các không gian tên, để xem tiền tố hoặc URI có cần thay đổi hay không. Nó sẽ thay đổi một tiền tố hoặc một URI độc lập với nhau, mà có thể không phải là điều mong muốn. Mặc dù vậy không phải là một vấn đề lớn.
Đối với phần còn lại, chỉ cần khớp mẫu trên Elem để recurse vào từng phần của XML. Ah, vâng, nó cũng thay đổi tiền tố của các phần tử. Một lần nữa, nếu đó không phải là những gì được mong muốn, thật dễ dàng để thay đổi.
Mã giả định không cần phải recurse vào các phần "khác" của XML - phần còn lại thường sẽ là phần tử Text. Ngoài ra, nó giả định không có không gian tên ở nơi khác. Tôi không có chuyên gia về XML, vì vậy tôi có thể sai cả về số lượng. Một lần nữa, sẽ dễ dàng thay đổi điều đó - chỉ cần làm theo mẫu.
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]
}
Điều này tạo ra kết quả không mong muốn. Phạm vi được thêm vào tất cả các phần tử. Đó là vì NamespaceBinding không định nghĩa phương thức equals, do đó sử dụng phương trình tham chiếu. Tôi đã mở một vé cho nó, 2138, đã bị đóng, vì vậy Scala 2.8 sẽ không có vấn đề này.
Trong khi đó, mã sau sẽ hoạt động bình thường. Nó giữ một bộ nhớ đệm của không gian tên. Nó cũng phân tách NamespaceBinding thành một danh sách trước khi xử lý nó.
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]
}