2011-01-12 17 views
14

Tôi đang xem xét các cách tiếp cận khác nhau cho dữ liệu marshalling/unmarshalling giữa Scala và XML, và tôi quan tâm đến việc nhận phản hồi của cộng đồng (tốt nhất là nền tảng kiến ​​thức/kinh nghiệm đầu tiên).Marshalling/unmarshalling XML trong Scala

Hiện tại chúng tôi đang sử dụng JAXB, điều này là tốt, nhưng tôi hy vọng sẽ có giải pháp Scala thuần túy. Tôi đang xem xét các phương pháp sau đây:

  1. Sử dụng Scala của tích hợp trong các cơ sở XML: Scala-> XML sẽ là dễ dàng, nhưng tôi đoán là theo một hướng khác sẽ khá đau đớn. Mặt khác, phương pháp này hỗ trợ logic dịch tùy ý.

  2. dữ liệu ràng buộc: scalaxb có vẻ là hơi non nớt vào lúc này và không xử lý sơ đồ hiện tại của chúng tôi, và tôi không biết về bất kỳ dữ liệu khác liên kết thư viện cho Scala. Giống như JAXB, cần thêm một lớp dịch để hỗ trợ các phép biến đổi liên quan.

  3. Bộ phối hợp trình xử lý XML: Bộ phối hợp bộ chọn pickler XML, nhưng hoạt động dự án gần đây đã ở mức thấp và tôi không biết trạng thái hiện tại là gì.

Câu hỏi:

  1. gì được kinh nghiệm của bạn với các phương pháp/thư viện tôi đã liệt kê?
  2. Ưu điểm và nhược điểm tương đối của mỗi loại là gì?
  3. Có phương pháp tiếp cận nào khác hoặc thư viện Scala mà tôi nên cân nhắc không?

Edit:

tôi đã thêm một số ghi chú trên những ấn tượng ban đầu của tôi về combinators Pickler trong câu trả lời của riêng tôi cho câu hỏi này, nhưng tôi vẫn rất quan tâm đến thông tin phản hồi từ những người thực sự biết các phương pháp khác trong chiều sâu. Những gì tôi hy vọng là một so sánh hơi toàn diện sẽ giúp các nhà phát triển lựa chọn phương pháp phù hợp với nhu cầu của họ.

+1

Nếu bạn có thể gửi cho tôi lược đồ đến (eed3si9n tại gmail), tôi có thể sửa lỗi scalaxb. –

Trả lời

5

Tôi khuyên bạn nên sử dụng các tính năng XML tích hợp của Scala. Tôi vừa triển khai deserialization cho cấu trúc tài liệu trông giống như sau:

val bodyXML = <body><segment uri="foo"><segment uri="bar" /></segment></body> 

Lưu ý rằng các đoạn có thể được lồng nhau.

Một bộ phận được thực hiện như sau:

case class Segment(uri: String, children: Seq[Segment]) 

Để deserialize XML, bạn làm như sau:

val mySegments = topLevelSegments(bodyXML) 

... và thực hiện các topLevelSegments chỉ là một vài dòng mã. Lưu ý đệ quy, được khai thác thông qua cấu trúc XML:

def topLevelSegments(bodyXML: Node): Seq[Segment] = 
    (bodyXML \ "segment") map { nodeToSegment } 

def nodeToSegment = (n: Node) => Segment((n \ "@uri")(0) text, childrenOf(n)) 

def childrenOf(n: Node): Seq[Segment] = (n \ "segment") map { nodeToSegment } 

Hy vọng điều đó sẽ hữu ích.

+0

Tôi cho rằng cách tiếp cận này không phải là lông như tôi mong đợi, nhưng tôi tự hỏi làm thế nào dễ dàng là cả hai để quy mô đến một lược đồ phức tạp hơn và duy trì theo thời gian. Một lợi thế nhất định của cả bộ kết hợp dữ liệu và bộ chọn lọc là bạn đồng thời xác định tuần tự hóa/deserialization để bạn không phải lo lắng về việc duy trì hai cơ quan song song của mã. –

+2

Mặc dù vậy, bất kỳ công nghệ bổ sung nào mà bạn trộn vào cơ sở mã của bạn đều có chi phí: một cú pháp để tìm hiểu, một tập hợp các thông báo lỗi để giải mã, một nhóm người dùng tham gia, có thể là một tinh chỉnh triển khai. Các 'bộ phận chuyển động' càng ít thì càng tốt. – David

-1

Viết một tệp scala.xml.Nút vào chuỗi không phải là vấn đề lớn. PrettyPrinter nên giải quyết các nhu cầu của bạn. scala.xml.XML.save() sẽ ghi vào một tệp và scala.xml.XML.write() kết quả đầu ra cho một Writer.

+2

Cảm ơn bạn đã trả lời, nhưng đây không phải là tất cả những gì tôi đang tìm kiếm. Tôi quan tâm đến việc chuyển đổi giữa các tài liệu XML và các mô hình đối tượng miền cụ thể. –

4

Để so sánh, tôi thực hiện David's example sử dụng combinators Pickler từ GData Scala Client thư viện:

def segment: Pickler[Segment] = 
    wrap(elem("segment", 
      attr("uri", text) 
      ~ rep(segment))) { // rep = zero or more repetitions 
     // convert (uri ~ children) to Segment(uri, children), for unpickling 
     Segment.apply 
    } { 
     // convert Segment to (uri ~ children), for pickling 
     (s: Segment) => new ~(s.uri, s.children toList) 
    } 

def body = elem("body", rep(segment)) 

case class Segment(uri: String, children: List[Segment]) 

Mã này là tất cả những gì là cần thiết để xác định cả hai hướng của bản dịch giữa Segment s và XML, trong khi một số lượng tương tự mã chỉ định một hướng của bản dịch khi sử dụng thư viện XML Scala. Theo tôi, phiên bản này cũng dễ hiểu hơn (một khi bạn biết DSL của người pickler). Tất nhiên, như David đã chỉ ra trong một bình luận, phương pháp này đòi hỏi một sự phụ thuộc bổ sung và một DSL khác mà các nhà phát triển phải làm quen với nó.

Dịch XML để phân đoạn đơn giản như

body.unpickle(LinearStore.fromFile(filename)) // returns a PicklerResult[List[Segment]] 

và dịch theo cách khác trông giống như

xml.XML.save(filename, body.pickle(segments, PlainOutputStore.empty).rootNode) 

Theo như thư viện combinator là có liên quan, nó có vẻ là trong hình dạng tốt và biên dịch trong Scala 2.8.1. Ấn tượng ban đầu của tôi là thư viện thiếu một vài thứ rất đẹp (ví dụ: một bộ tổ hợp oneOrMore) có thể được khắc phục khá dễ dàng. Tôi đã không có thời gian để xem nó xử lý tốt đầu vào xấu như thế nào, nhưng cho đến nay nó có vẻ đủ cho nhu cầu của tôi.

+0

"một hoặc nhiều" Không phải là những gì 'rep1' làm gì? – soc

+0

@soc Tôi giả sử bạn đang đề cập đến trình kết hợp trình phân tích cú pháp 'rep1' trong thư viện chuẩn. Thật không may, không có bộ kết hợp như vậy trong thư viện bộ chọn XML. –