2012-03-31 13 views
6

Supongamos que tengo una API REST en Java y admite respuestas que son JSON o XML. Las respuestas contienen los mismos datos, pero la forma no es idéntica. Por ejemplo, en JSON que podría tener:admite respuestas REST tanto xml como json en clojure

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

Mientras que en XML que tiene este aspecto:

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

Lo que quiere decir que algunos valores son atributos de la persona, y otros son elementos secundarios. En Java, utilizando JAXB y Jackson, es fácil de ocultar tales diferencias con anotaciones sobre los objetos del modelo, por ejemplo:

public class Person { 
    @XmlAttribute 
    String name; 

    @XmlAttribute 
    Integer age; 

    @XmlElement 
    String hometown; 
} 

JAXB lee las anotaciones, y Jackson utiliza los nombres de los campos de averiguar qué hacer. Entonces, con un solo modelo, es fácil admitir múltiples formatos de salida.

Así que mi pregunta es cómo hacer lo mismo en clojure. Sé que hay clj-json que puede convertir fácilmente mapas y vectores de clojure a json (utilizando jackson si no me equivoco). Y sé que hay tanto clojure.xml.emit como clojure.contrib.xml.prxml que pueden deserializar los vectores & de mapas a XML. Pero a menos que esté equivocado, no creo que estos dos trabajen juntos muy bien.

Porque prxml espera que los nodos xml se expresen como vectores, y que los atributos xml se expresen como un mapa, fundamentalmente diferente del funcionamiento de clj-json, donde los vectores representan matrices y los mapas representan objetos. Y clojure.core.emit espera un mapa en el formulario {:tag :person :attrs {:name "Bob" :age 24} :content ...}, que de nuevo es completamente diferente de lo que quiere clj-json.

Lo único que puedo pensar es formatear las estructuras de datos para prxml en mi código, y luego escribir una función que transforma la estructura de datos a lo que quiere clj-json cuando el tipo de respuesta es JSON. Pero eso parece un poco cojo. Preferiría que hubiera un par de bibliotecas JSON y XML que fueran compatibles en la forma en que JAXB y Jackson lo son.

Ideas?

+0

¿Tal vez eche un vistazo a la nueva [data.xml] (https://github.com/clojure/data.xml)? – Jeremy

+0

que aún parece requerir que los datos se formateen como lo espera clojure.xml, p. '{: tag: person: attrs {: name" Bob ": age 24}: content ...}'. – Kevin

+0

Todavía no he usado data.xml, pero pensé que sería posible crear tu propio emisor para escribir el XML como lo deseas. Aunque no estoy 100% seguro. – Jeremy

Respuesta

5

Mucho depende de cómo elija representar modelos en su código.

Supongamos que utiliza registros. Aquí hay un ejemplo artificial de cómo puede "anotar" un registro y proporcionar serializadores para XML y 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)) 

Uso:

> (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 

Una macro se podría crear para definir un registro y sus atributos de XML en un comunicado de la chamusquina. Por ejemplo,

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

muy agradable, gracias. – Kevin

Cuestiones relacionadas