2012-03-31 8 views
6

私はJavaでREST APIを持っており、JSONまたはXMLのいずれかの応答をサポートしています。応答には同じデータが含まれていますが、フォームは同一ではありません。たとえば、JSONに私が持っているかもしれません:XMLで、それはこのようになりますはxmlとjsonの両方をサポートしていますclojureのREST応答

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

に対し:

一部の値が人の属性があり、そして他の人が子要素であると言うことです
<persons> 
    <person name="bob" age="24"> 
     <hometown>New York</hometown> 
    </person> 
</persons> 

。 Javaでは、JAXBとジャクソンを使用して、それは例えば、モデルオブジェクト上の注釈と、このような差異を隠蔽するのは簡単です:

public class Person { 
    @XmlAttribute 
    String name; 

    @XmlAttribute 
    Integer age; 

    @XmlElement 
    String hometown; 
} 

JAXB注釈を読み、ジャクソンは何をすべきかを把握するためにフィールド名を使用しています。したがって、単一のモデルで複数の出力形式をサポートするのは簡単です。

私の質問は、どのようにclojureで同じことをするかです。 cljjsonがclojureマップとベクトルをjsonに簡単に変換できるclj-jsonがあることを知っています(誤解していなければjacksonを使用します)。そして私は、&ベクトルをXMLに逆シリアル化できるclojure.xml.emitとclojure.contrib.xml.prxmlの両方があることを知っています。しかし、私が間違っていない限り、私はこれら2つが一緒にうまく機能するとは思わない。

prxmlは、xmlノードがベクトルとして表現され、xml属性がマップとして表現されることを想定しているため、ベクトルが配列を表し、マップがオブジェクトを表すclj-jsonの動作とは基本的に異なります。そして、clojure.core.emitは、{:tag :person :attrs {:name "Bob" :age 24} :content ...}の形式のマップを期待しています。これはclj-jsonが望むものとはまったく異なっています。

私の考えることは、自分のコードでprxmlのデータ構造体をフォーマットし、レスポンスの種類がJSONのときにclj-jsonが望むものにデータ構造を変換する関数を書くことだけです。しかし、それは色あせているようです。 JAXBとジャクソンが互換性のあるJSONとXMLライブラリのペアがあれば、私は好きです。

アイデア?

+0

新しい[data.xml](https://github.com/clojure/data.xml)をご覧ください。 – Jeremy

+0

これは、clojure.xmlのようにデータをフォーマットする必要があると思われます。 '{:tag:person:attrs {:名前" Bob ":年齢24}:コンテンツ...}'。 – Kevin

+0

まだdata.xmlは使用していませんが、自分のエミッタをビルドしてXMLを書き出すことができると考えました。私は100%確信していません。 – Jeremy

答えて

5

多くのことは、コード内でモデルを表現する方法によって異なります。

レコードを使用するとします。ここでは、レコードに「注釈を付ける」方法と、XMLと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)) 

使用法:

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

マクロが焦がす文で記録し、そのXML属性を定義するために作成することができます。たとえば、

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

非常に良い、ありがとう。 – Kevin

関連する問題