1

XML ドキュメントがあるタスクが目の前にあり、それを体系的な方法で別の XML ドキュメントに変換する必要があります。タグ Foo をタグ Bar に変更し、name="frob"属性を持つすべての Qux タグを Frob タグに変更するなどです。 . XSLT の使用方法については何も知りませんが、ツリーベースのデータに対して一連の変換を実行する必要がある場合は、Lisp が得意なことのように思えます!

だから私はXMLの塊を持っています - 例えば:

<Object>
    <field name="id">100520</field>
    <field name="type_id">77</field>
    <field name="has_extras"></field>
    <field name="author_id">7</field>
    <field name="summary">To Sir Duke, with love</field>
</Object>

私はそれを丸呑みしてxml-parse tag取得します:

(Object nil "\n        "
     (field
     ((name . "id"))
     "100520")
    "\n        "
    (field
     ((name . "type_id"))
     "77")
    "\n        "
    (field
     ((name . "has_extras")))
    "\n        "
    (field
     ((name . "author_id"))
     "7")
    "\n        "
    (field
     ((name . "summary"))
     "To Sir Duke, with love")
    "\n    ")

その木をどう処理して自分の望む形にするかを考え出すのに苦労しています。assoc私の現在の試みはもろいです -と 機能に重きを置いていcxrます。CLdestructuring-bindは私が欲しいもののようですが、それを適用する方法がわかりません。上記の構造を次のように変換しようとしています。

(Object
  (id "100520")
  (type_id "77")
  (has_extras "")
  (author_id "7")
  (summary "To Sir Duke, with love"))
  • 本当にdestructuring-bind必要なツールですか?
  • もしそうなら、データのある形状から別の形状に取得するには、どのように適用すればよいですか?
  • そうでない場合、これに適したツールは何ですか?
4

1 に答える 1

4

確かにそれdestructuring-bindはこの仕事までではありませんが、Emacs 24ではpcase、次のようにパターンマッチングマクロを使用して非常に簡潔に行うことができます。

(require 'cl)                ;; for `mapcan'
(require 'pcase)

(defun xslt-in-elisp (xml)
  (pcase xml
    (`(Object . ,rest)
     `(Object . ,(mapcan #'xslt-in-elisp rest)))

    (`(field ((name . ,name)))
     `((,(intern name) "")))

    (`(field ((name . ,name)) ,value)
     `((,(intern name) ,value)))

    (_ nil)))

(xslt-in-elisp
 '(Object nil "\n        "
          (field ((name . "id")) "100520")
          "\n        "
          (field
           ((name . "type_id"))
           "77")
          "\n        "
          (field
           ((name . "has_extras")))
          "\n        "
          (field
           ((name . "author_id"))
           "7")
          "\n        "
          (field
           ((name . "summary"))
           "To Sir Duke, with love")
          "\n    "))

これは次のように評価されます。

(Object
 (id "100520")
 (type_id "77")
 (has_extras "")
 (author_id "7")
 (summary "To Sir Duke, with love"))

仕組み:pcaseパターンマッチに値を取り、一連の句(PATTERN VALUE)を順番に試します。で詳細を調べることができますM-x describe-function pcaseが、基本的にパターンは一致させたいもののように見えます。バッククォート構文を使用して、バインドするパターン一致変数である部分と、リテラルシンボルとして一致する部分を指定します。だから、最初のルール

`(Object . ,rest)

Object最初のシンボルとしてのリストと一致し、変数restを残りの要素にバインドします。ルール

`(field ((name . ,name))` 

field名前はあるがコンテンツがないタグのS-expと一致します(has_extras例のように)。等々。最後のルール、は、これらのルールに一致しないものを_返します。nil各ルールの右側は、任意のLisp式にすることができます。この種の変換では、バッククォートとアンクォートを使用するのが最も便利です。これには、テンプレートが一致するルールと同じように見えるというボーナスがあります。

少し注意が必要なのは、の子ノードの変換された値をどのように累積するかです(Object ...)mapcarそれらを繰り返し処理していた場合、元々空白やその他のゴミの文字列があった場所に不要なsnilが表示されることになります。解決策は、fieldタグのルールで1要素のリストを返し、パッケージから使用mapcanしてclこれらの1要素のリストを連結することです。のようなガベージ要素nilと空白文字列はルールに一致する_ため、空のリストに変換されて結果から消えます。

(Object ...)トランスフォーマーを再帰関数として作成しましたが、堅牢性のために、sexpsのみに一致するトップレベルのトランスフォーマーとsexpsのみに一致する別のトランスフォーマーに簡単に分割できます(field ... )

于 2012-10-31T23:21:04.103 に答える