2011-01-12 13 views
14

Rozglądam się przy różnych podejściach do zestawiania/odczytywania danych pomiędzy Scala i XML, i jestem zainteresowany uzyskaniem informacji zwrotnych od społeczności (najlepiej opartych na wiedzy z pierwszej ręki/doświadczeniu).Zbieranie/rozprowadzanie XML w Scali

Obecnie używamy JAXB, co jest w porządku, ale mam nadzieję na czyste rozwiązanie Scala. Zastanawiam następujące podejścia: wbudowany w obiektach XML

  1. Skorzystaj Scala: rozwiązanie dopasowane> XML będzie łatwe, ale wydaje mi się, że drugi kierunek byłoby dość bolesne. Z drugiej strony takie podejście wspiera arbitralną logikę tłumaczenia.

  2. Wiązanie danych: scalaxb wydaje się być nieco niedojrzałe w tej chwili i nie obsługuje naszego obecnego schematu, a ja nie wiem o wszelkich innych danych wiążących bibliotekę Scala. Podobnie jak JAXB, wymagana jest dodatkowa warstwa translacji do obsługi zaangażowanych transformacji.

  3. XML Pickler kombinatorów: Biblioteka GData Scala Client zapewnia kombinatorów Pickler XML, ale aktywność najnowszy projekt był niski i nie wiem, jaki jest aktualny status.

Pytania:

  1. Jakie są wasze doświadczenia z metodami/bibliotek Mam na liście?
  2. Jakie są względne zalety i wady każdego z nich?
  3. Czy są jakieś inne podejścia lub biblioteki Scala, które powinienem wziąć pod uwagę?

Edit:

dodałem kilka uwag na temat moich pierwszych wrażeń kombinatorów Pickler we własnej odpowiedzi na to pytanie, ale ja nadal jestem bardzo zainteresowany zwrotne od kogoś, kto rzeczywiście zna różne podejścia dogłębnie. Mam nadzieję, że to w pewnym stopniu kompleksowe porównanie, które pomoże programistom wybrać właściwe podejście do ich potrzeb.

+1

Jeśli możesz przesłać mi schemat do (eed3si9n na gmail), być może uda mi się naprawić scalaxb. –

Odpowiedz

5

Polecam używanie wbudowanych funkcji XML Scala. Właśnie zaimplementowałem deserializację dla struktury dokumentu, która wygląda następująco:

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

Należy zauważyć, że segmenty mogą być zagnieżdżane jeden w drugim.

Segment jest realizowany w następujący sposób:

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

deserializacji XML, to zrobić:

val mySegments = topLevelSegments(bodyXML) 

... i realizacja topLevelSegments jest zaledwie kilka linijek kodu. Zwróć uwagę na rekurencję, która przekopuje się przez strukturę 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 } 

Nadzieję, że pomaga.

+0

Przypuszczam, że to podejście nie jest tak owłosione, jak się spodziewałem, ale zastanawiam się, jak łatwo można skalować do bardziej złożonego schematu i utrzymywać go z czasem. Zdecydowaną zaletą zarówno łączenia danych, jak i kombajnów kolekcjonerskich jest to, że jednocześnie określasz serializację/deserializację, dzięki czemu nie musisz się martwić utrzymywaniem dwóch równoległych ciał kodu. –

+2

To prawda, że ​​jakakolwiek dodatkowa technologia, którą miksujesz w bazie kodu, niesie ze sobą obciążenie: składnia do nauki, zestaw komunikatów o błędach do odszyfrowania, grupa użytkowników do przyłączenia się, ewentualnie modyfikacja wdrożenia. Im mniej "ruchomych części", tym lepiej. – David

-1

Pisanie pliku scala.xml.Node do ciągu znaków nie jest wielkim problemem. PrettyPrinter powinien zadbać o Twoje potrzeby. scala.xml.XML.save() napisze do pliku i scala.xml.XML.write() wyjścia do Writer.

+2

Dzięki za odpowiedź, ale to wcale nie jest to, czego szukałem. Interesuje mnie konwersja między dokumentami XML a modelami obiektów specyficznych dla domeny. –

4

Dla porównania, I wdrożone David's example używając kombinatorów Pickler z GData Scala Client Biblioteka:

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]) 

Kod ten jest wszystko, co niezbędne jest określenie obu kierunków tłumaczenia między Segment s oraz XML, natomiast podobną kwotę kodu określa tylko jeden kierunek tłumaczenia podczas korzystania z biblioteki Scala XML. Moim zdaniem, ta wersja jest również łatwiejsza do zrozumienia (gdy poznasz DSL). Oczywiście, jak zauważył David w komentarzu, takie podejście wymaga dodatkowej zależności i kolejnego DSL, z którym programiści muszą się zapoznać.

Przełożenie XML do segmentów jest tak proste jak

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

i tłumaczenia odwrotnie wygląda

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

ile biblioteka syntezatora to dotyczy, wydaje się być w przyzwoitym stanie i kompiluje w Scali 2.8.1. Moje pierwsze wrażenie jest takie, że w bibliotece brakuje kilku nicetek (na przykład kombinator oneOrMore), które można dość łatwo naprawić. Nie miałem czasu, aby zobaczyć, jak dobrze radzi sobie ze złym wkładem, ale jak na razie wygląda na wystarczający dla moich potrzeb.

+0

"jeden lub więcej" Czy to nie robi 'rep1'? – soc

+0

@soc Zakładam, że masz na myśli kombinator 'rep1' w bibliotece standardowej. Niestety nie ma takiego kombinatora w bibliotece modułów zbierających XML. –