2012-03-31 29 views
6

假设我在java中有一个REST API,并且它支持JSON或XML响应。答案包含相同的数据,但形式不同。例如,在JSON我可能有:支持clojure中的xml和json REST响应

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

而在XML它看起来像这样:

<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中做同样的事情。我知道有clj-json可以很容易地将clojure地图和向量转换为json(如果我没有弄错,可以使用jackson)。我知道clojure.xml.emit和clojure.contrib.xml.prxml都可以将映射向矢量反序列化为XML。但除非我错了,否则我不认为这两者能够很好地协同工作。

由于prxml期望xml节点表示为向量,xml属性表示为映射,与clj-json的工作基本上不同,其中向量表示数组,地图表示对象。 clojure.core.emit预计地图的形式为{:tag :person :attrs {:name "Bob" :age 24} :content ...},这与clj-json想要的完全不同。

我能想到的唯一办法是在我的代码中为prxml格式化数据结构,然后编写一个函数将数据结构转换为clj-json在响应类型为JSON时所需的数据结构。但那似乎有些跛脚。如果有一对JSON和XML库以JAXB和Jackson的方式兼容,我宁愿选择它。

想法?

+0

也许看看新的[data.xml](https://github.com/clojure/data.xml)? – Jeremy 2012-04-01 04:42:38

+0

,它似乎仍然需要像clojure.xml那样格式化数据,例如'{:tag:person:attrs {:name'Bob':24岁}:content ...}'。 – Kevin 2012-04-01 05:33:17

+0

我还没有使用data.xml,但我的想法是,可以建立自己的发射器来根据需要写出XML。不过,我不是100%肯定的。 – Jeremy 2012-04-01 13:40:46

回答

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 2012-04-03 01:59:50