2012-05-15 5 views
2

У меня есть необходимость сериализации структуры данных Clojure для XML, но я натыкаюсь на то, как испускать коллекцию. Для того, чтобы сделать это более конкретным, предположим, что у меня есть карта Clojure следующим образом:Извлечение XML из Clojure с использованием clojure.data.xml.

(def parking-lot {:parking-lot #{ 
         {:car {:color "red", :make "nissan", :year 2003}} 
         {:car {:color "blue", :make "toyota", :year 2001}} 
         {:car {:color "black", :make "honda", :year 2010}}}}) 

Это просто карта с одним элементом, значение которого представляет собой набор «автомобиль» пунктов. Теперь, давайте предположим, что я хочу, чтобы сериализовать эту карту, чтобы получить следующий XML:

<? xml version="1.0" ?> 
    <parking-lot> 
     <car color="red" make="nissan" year="2003" /> 
     <car color="blue" make="toyota" year="2001" /> 
     <car color="black" make="black" year="2010" /> 
    </parking-lot> 

Поиск в Интернете документы о том, как лучше всего разобрать/испускают XML в Clojure привели меня к clojure.data.xml библиотеке, так это то, что я используя.

Вот простой пример того, как использовать clojure.data.xml испустить некоторый XML:

REPL> (use 'clojure.data.xml) 

     => nil 
    REPL> (emit-str (element :parking-lot {})) 

     => "<?xml version=\"1.0\" encoding=\"UTF-8\"?> 
      <parking-lot></parking-lot>" 

несколько более сложный пример:

REPL> (emit-str (element :parking-lot {} 
       (element :car {:color "yellow" :make "ford" :year "2000"}) 
       (element :car {:color "purple" :make "chevrolet" :year "1977"}))) 

     => "<?xml version=\"1.0\" encoding=\"UTF-8\"?> 
      <parking-lot> 
       <car color=\"yellow\" year=\"2000\" make=\"ford\"></car> 
       <car color=\"purple\" year=\"1977\" make=\"chevrolet\"></car> 
      </parking-lot>" 

Здесь вы видите, что третий аргумент функция «element» действительно может быть переменным числом вызовов «element». Не смотря глубоко на документы для функции «element», я предполагаю, что я предположил, что «элемент» будет перегружен, чтобы также взять последовательность элементов элемента в качестве третьего аргумента. И так, не глядя на документы, я просто пошел вперед и взбитыми небольшой блок кода, который будет делать только что:

REPL> (emit-str (element :parking-lot {} 
       (map (fn [car] (let [carattrs (:car car)] 
         (element :car {:color (:color carattrs), 
             :make (:make carattrs), 
             :year (:year carattrs)}))) 
        (:parking-lot parking-lot)))) 

Я надеялся, что это будет работать, но, увы, это не так. Проблема в том, что третий аргумент функции «element» не может быть самой последовательностью.

Итак, на данный момент я немного тупик относительно того, что делать. Я все еще относительно новичок в Lisp и Clojure, так что легко, если моя следующая мысль будет придирчивой. Итак, моя мысль, будет ли это прецедентом для макроса? I.e., должен ли я создать макрос, который принимает в качестве своего ввода набор (или любой столбец для этого) машин и выводит его элементы как отдельные s-выражения? Так, например, я имею в виду, на мой взгляд макроса, который ведет себя так:

REPL> (defmacro flatten-as-elements [coll-of-cars] ...) 
    REPL> (flatten-as-elements #{ {:color "blue" :model "honda" year "2000"} 
            {:color "yellow" :model "ford" year "2011"}}) 
     => (element :car {:color "blue" :model "honda" year "2000"}) 
      (element :car {:color "yellow" :model "ford" year "2011"}) 

В моем уме, по крайней мере, на выходе такого макроса будет соответствовать как 3-й аргумент функции «элемент» , и создаст мою желаемую цель. Конечно, я беспокоюсь, что есть совершенно очевидное решение, которое я игнорирую, и что использование макроса здесь ошибочно. Любая помощь сообщества SO очень ценится! Заранее спасибо!

-Поль

ответ

5

Вы можете использовать apply сращивать аргументы в вызов функции:

(emit-str 
(apply element :parking-lot {} 
     (map (fn [car] (let [carattrs (:car car)] 
         (element :car {:color (:color carattrs), 
             :make (:make carattrs), 
             :year (:year carattrs)}))) 
          (:parking-lot parking-lot)))) 
6

Преобразования вы просили не возможно вообще, потому что у вас есть «скрытая» информация о том, что, как предполагается, атрибут и то, что суб-элемент. Вы можете написать эту пользовательскую функцию flatten-as-elements или независимо от того, что она есть, и тогда вы сможете сериализовать, если вы введете кучу знаний о домене на протяжении всего процесса.

Но на самом деле, вы должны использовать один из двух популярных «представлений» из XML в Clojure: либо записать данные в

{:tag :parking-lot 
:content [{:tag :car :attrs {:color "red", :make "nissan", :year 2003}} 
      {:tag :car :attrs {:color "blue", :make "toyota", :year 2001}} 
      {:tag :car :attrs {:color "black", :make "honda", :year 2010}}]} 

который я думаю, данные.XML может испускать для вас достаточно просто, либо использовать гораздо более лаконичный стиль икоты, с векторами для тегов и списков для нескольких элементов:

[:parking-lot (list [:car {:color "red", :make "nissan", :year 2003}] 
        [:car {:color "blue", :make "toyota", :year 2001}] 
        [:car {:color "black", :make "honda", :year 2010}])] 

, который можно распечатать с (emit (sexp-as-element [...])).

Смежные вопросы