2012-03-31 16 views
6

Powiedz, że mam interfejs REST API w java, i obsługuje odpowiedzi, które są albo JSON albo XML. Odpowiedzi zawierają te same dane, ale formularz nie jest identyczny. Na przykład, w json może mam:obsługuje zarówno odpowiedź xml i json REST w clojure

{ 
    "persons": [ 
     { 
      "name":"Bob", 
      "age":24, 
      "hometown":"New York" 
     } 
    ] 
} 

Natomiast w XML wygląda następująco:

<persons> 
    <person name="bob" age="24"> 
     <hometown>New York</hometown> 
    </person> 
</persons> 

To znaczy, że niektóre wartości są atrybuty na osobę, a inne są elementy potomne. W języku Java, przy użyciu JAXB i Jackson, to jest łatwe do ukrycia takich różnic z adnotacji na modelu obiektów, na przykład:

public class Person { 
    @XmlAttribute 
    String name; 

    @XmlAttribute 
    Integer age; 

    @XmlElement 
    String hometown; 
} 

JAXB odczytuje adnotacje, a Jackson używa nazwy pól, aby dowiedzieć się, co zrobić. Tak więc w przypadku jednego modelu można łatwo obsługiwać wiele formatów wyjściowych.

Moje pytanie brzmi: jak zrobić to samo w clojure. Wiem, że istnieje clj-json, który może z łatwością przekonwertować mapy i wektory clojure na json (używając jacksona, jeśli się nie mylę). I wiem, że są clojure.xml.emit i clojure.contrib.xml.prxml, które mogą deserializować mapy wektorów do W201. Ale jeśli się nie mylę, nie sądzę, żeby ci dwoje dobrze ze sobą współpracowali.

Ponieważ prxml oczekuje, że węzły xml będą wyrażone jako wektory, a atrybuty xml będą wyrażone jako mapa, zasadniczo inne niż działanie clj-json, gdzie wektory reprezentują tablice, a mapy reprezentują obiekty. A clojure.core.emit spodziewa się mapy w postaci {:tag :person :attrs {:name "Bob" :age 24} :content ...}, która jest znowu zupełnie inna niż to, czego chce clj-json.

Jedyne co mogę myśleć to sformatować struktury danych dla prxml w moim kodzie, a następnie napisać funkcję, która przekształca strukturę danych do tego, czego chce clj-json, gdy typem odpowiedzi jest JSON. Ale wydaje się to trochę kiepskie. Wolałbym, gdyby istniała para bibliotek JSON i XML, które byłyby kompatybilne w taki sposób, jak JAXB i Jackson.

Pomysły?

+0

Może przyjrzeć się nowym [data.xml] (https://github.com/clojure/data.xml)? – Jeremy

+0

nadal wydaje się wymagać sformatowania danych, takich jak clojure.xml, np. '{: tag: person: attrs {: name" Bob ": wiek 24}: treść ...}'. – Kevin

+0

Nie używałem jeszcze pliku data.xml, ale myślałem, że może być możliwe zbudowanie własnego emiterów, aby zapisać kod XML, jak tego chcesz. Nie jestem jednak w 100% pewny. – Jeremy

Odpowiedz

5

Wiele zależy od sposobu, w jaki reprezentujesz modele w kodzie.

Załóżmy, że używasz rekordów. Oto przykład, w jaki sposób można "opisać" rekord i udostępnić serializatory dla XML i JSON.

;; Depends on cheshire and data.xml 
(ns user 
    (:require [cheshire.core :as json] 
      [clojure.data.xml :as xml])) 

(defrecord Person [name age hometown]) 
(defrecord Animal [name sound]) 

(def xml-attrs {Person [:name :age] 
       Animal [:name]}) 

(defn record->xml-data [rec] 
    (let [tag (-> rec class .getSimpleName .toLowerCase keyword) 
     attrs (select-keys rec (xml-attrs (class rec))) 
     content (for [[k v] rec 
         :when (not (contains? attrs k))] 
        (xml/element k nil (str v)))] 
    (apply xml/element tag attrs content))) 

(defn record->xml [rec] 
    (xml/emit-str (record->xml-data rec))) 

(defn record->json [rec] 
    (json/generate-string rec)) 

Zastosowanie:

> (def bob (Person. "Bob" 24 "New York")) 
#'user/bob 

> (println (record->xml bob)) 
<?xml version="1.0" encoding="UTF-8"?><person age="24" name="Bob"><hometown>New York</hometown></person> 
nil 

> (println (record->json bob)) 
{"name":"Bob","age":24,"hometown":"New York"} 
nil 

> (println (record->xml (Animal. "Fido" "Bark!"))) 
<?xml version="1.0" encoding="UTF-8"?><animal name="Fido"><sound>Bark!</sound></animal> 
nil 

makro mogą być tworzone w celu określenia jego rekord i atrybutów XML w oświadczeniu przypalić. Np

(defrecord-xml Person [^:xml-attr name ^:xml-attr age hometown]) 
+0

bardzo ładne, dzięki. – Kevin