2009-07-07 28 views
5

Tôi đang sử dụng scala để tải tệp XML từ tệp qua phương thức scala.xml.XML.loadFile(). Các tài liệu tôi đang làm việc với không gian tên đã được xác định và tôi muốn thay đổi không gian tên thành một cái gì đó khác bằng cách sử dụng scala. Ví dụ: tài liệu có xmlns là "http://foo.com/a" với tiền tố "a" - tôi muốn thay đổi không gian tên và tiền tố cho tài liệu thành "http://foo.com/b" và "b" tương ứng.Thay đổi không gian tên XML bằng Scala

Có vẻ dễ dàng và tôi cảm thấy như tôi thiếu điều gì đó hiển nhiên ở đây. Tôi không gặp sự cố khi nhận không gian tên từ trả lại Elem từ phương thức được tham chiếu loadFile().

Trả lời

9

Đâ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] 
} 
0

Lỗi nhỏ tại đây. Thuộc tính cũng có thể có tên đủ điều kiện. Bạn cần phải kiểm tra những người là tốt.

Các vấn đề liên quan