2

次のマップをxmlに変換しようとしています(ベクトル値を持つキーは、ベクトル内の各要素に対してxml内のキーを繰り返す必要があります)

(use 'clojure.xml)
(defn map-to-xml2 [k v]
      (cond
         (nil? k)    (for [[e a] v] {:tag e :content (map-to-xml2 e a)})
         (map? v)    (for [[e a] v] {:tag e :content (map-to-xml2 e a)})
         (vector? v) (for [x v] {:tag k :content (for [[e a] x] {:tag e :content (map-to-xml2 e a)})})
         :else       [(str v)]))

(def studios [{:company {:name "Acme Corp" :id 1 :rank 20 :employee 
                  [{:fname "Mark" :lname "Jones"} {:fname "Leroy" :lname "Bell"}]}}
              {:company {:name "Eastwood Studios" :id 2 :rank 35 :employee 
                  [{:fname "Lee" :lname "Marvin"} {:fname "Clint" :lname "Eastwood"}]}}])

(->> studios first (map-to-xml2 nil) first emit with-out-str (spit "acme.xml"))
(->> studios second (map-to-xml2 nil) first emit with-out-str (spit "eastwood.xml"))

次のxmlを取得します

<?xml version='1.0' encoding='UTF-8'?>
<company>
  <rank>35</rank>
  <employee>
    <employee>
      <lname>Marvin</lname>
      <fname>Lee</fname>
    </employee>
    <employee>
      <lname>Eastwood</lname>
      <fname>Clint</fname>
    </employee>
  </employee>
  <name>Eastwood Studios</name>
  <id>2</id>
</company>

私が実際に石鹸を介して送信する必要があるのは

<?xml version='1.0' encoding='UTF-8'?>
<company>
  <name>Eastwood Studios</name>
  <id>2</id>  
  <rank>35</rank>
  <employee>
    <lname>Marvin</lname>
    <fname>Lee</fname>
  </employee>
  <employee>
    <lname>Eastwood</lname>
    <fname>Clint</fname>
  </employee>
</company>

上記を修正するにはどうすればよいですか?

私はExcelファイルからデータを読み込もうとしています.同じIDを持つ行または行のグループごとにwebservice呼び出しを行い、スプレッドシートを応答で更新します. 上記は、webservice 呼び出しに必要な xml を生成することです。

4

2 に答える 2

1

あなたがおそらくすでに知っているあなたの主な問題は、コンテンツ(すなわちプログラム本体内)がベクトルである場合、そのすべてのタグとコンテンツを真に表現するためにv何らかの表現をしなければならないという(for ...)ことです。(map ...)ただし、そうすることで、厄介なパレン内にパッケージ化された一連のタグを生成します。私が言えることから、に渡す正しい構造を取得するには、それらを「アンパレンス」する必要があります(emit-element ...)

したがって、以下の私のコードでは(mapcat to-xml ...)、ネストが必要な場所に式があります。これは、連続するアイテムに対して操作を実行し、それらをすべて連結するためです。残念ながら、以前は単一アイテムの返品であったものをベクトル(または必要に応じてリスト)内に配置する必要があります。そのため、(map? v)trueの場合、または:else発生した場合、式全体(tag-xml ...)がベクトルにラップされます。すべての返品はconcat他の返品と一緒に編集されます。

私はあなたのために働く何かを見つけたと思います。私の意見では、トップレベルの呼び出し、つまりコードで行う呼び出しの処理方法が気に入らないので、それは素晴らしいことではありません(ただし、後で説明します)

(defn tag-xml
  [tag content]
  {:tag tag
   :content content})

(defn to-xml
  ([[k v]] ;//This form of to-xml is for the sake of nested calls
    (cond
      (map? v) [(tag-xml k (mapcat to-xml v))]
      (vector? v) (for [x v] (tag-xml k (mapcat to-xml x)))
      :else [(tag-xml k [(str v)])]))
  ([k v] ;//This form of to-xml is only for the sake of the top level call
    (tag-xml k (if (map? v) (mapcat to-xml v) [(str v)]))))

ヘルパー関数を追加したことに注意してくださいtag-xml。これは、本体をto-xmlよりクリーンで小さくするためだけのものでした。

これはあなたがそれを使うかもしれない方法です(あなたの場合、あなたはprintlnいくつかのspit呼び出しに置き換えますが):

=> (->> studios ffirst (apply to-xml) emit with-out-str println))
<?xml version='1.0' encoding='UTF-8'?>
<company>
<rank>
20
</rank>
<employee>
<lname>
Jones
</lname>
<fname>
Mark
</fname>
</employee>
<employee>
<lname>
Bell
</lname>
<fname>
Leroy
</fname>
</employee>
<name>
Acme Corp
</name>
<id>
1
</id>
</company>
=> nil

したがって、トップレベルから既存のハッシュマップに適切に呼び出すためには、それdataを行う必要があります(apply to-xml (first data))。データをハッシュマップとして使用する代わりに、ベクトルとして構造化することで、これを回避できます。あなたの例では、これはの各スタジオ[:company ...]の代わりに次のようになります。次に、代わりに、次のような関数を使用できます。{:company ...}studios(first (to-xml data))

それでも、これは私が望むほどエレガントではありません。おそらく解決策はto-xml、トップレベルの呼び出しを実行する関数と、-to-xmlその後それを処理する他の関数を持つことです。ユーザーとしては、を使用するだけですがto-xml、すべてのハードワークはで行われ-to-xmlます。私もその考えに夢中ではありません。さらに別のアイデアは、あなたがやったことと少し似たようなことをすることです。最初の引数が等しい場合nil、それはトップレベルの呼び出しであるかのように機能します。うーん。

とにかく、それは機能するので、それは何かです。

編集
順序を保持したい場合は、データを再定義するか、で処理する前にデータを変換する必要がありto-xmlます。として書かれたものの順序に依存することはできません{...}。マップとして保持したい場合は、配列マップまたはソートされたマップにすることで順序を取得できます。

配列マップにするために再定義すると、次のようになります。

(def studios [(array-map :company (array-map :name "Acme Corp" :id 1 :rank 20
                         :employee [(array-map :fname "Mark" :lname "Jones")
                                    (array-map :fname "Leroy" :lname "Bell")]))
              (array-map :company (array-map :name "Eastwood Studios" :id 2 :rank 35
                         :employee [(array-map :fname "Lee" :lname "Marvin")
                                    (array-map :fname "Clint" :lname "Eastwood")]))])

基本的に、以前はどこにでも{...}あり(array-map ...)ました。この時点で、私はあなたのためにそれをするためにマクロを書くことをわざわざ試みないでください、それはうまくいきません(これに関する私の質問についてはここを見てください)。ソートされたマップを使用したい場合は、ハードコードされた順序を返すtrueか、それfalseに基づいた述語コンパレータを作成する必要があります。これは私には少し奇妙に思えます。

ここで、データを変換する場合は、キーの順序とネストされた順序を含む別のデータ構造が必要になります。何かのようなもの:

(def studio-order-specs {:company [:name :id :rank {:employee [:lname :fname:]}]})

手元に変換関数はありませんが、このデータ構造を使用して、ハッシュマップを指定された順序の配列マップに変換するものを記述できるはずです。(これを使用して、指定された順序のソートされたマップに変換することもできますが、これも、私の意見では、エレガントでない方法で述語を定義することによって行われます。)

于 2012-07-31T14:29:53.457 に答える
1

入力には、マップ内の :employee キーワードの下にある従業員のリストを含む入れ子の追加レイヤーが 1 つあります。その囲み構造をリストに変更すると、ツリー全体を 1 レベルフラット化できます。

{:company {:name "Acme Corp" :id 1 :rank 20 :employee 
              [{:fname "Mark" :lname "Jones"} {:fname "Leroy" :lname "Bell"}]}}

次のようになります。

{:company [{:name "Acme Corp"} 
           {:id 1} {:rank 20} 
           {:fname "Mark" :lname "Jones"} 
           {:fname "Leroy" :lname "Bell"}]}
于 2012-07-30T22:47:20.020 に答える