我面前有一个任务,我有一个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
destructuring-bind
功能。 CL的(Object
(id "100520")
(type_id "77")
(has_extras "")
(author_id "7")
(summary "To Sir Duke, with love"))
似乎是我想要的,但我
无法弄清楚如何应用它。我正试图改变上面的结构
进入这个:
destructuring-bind
答案 0 :(得分: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
迭代它们,我们最终会得到不需要的nil
s,其中最初有空格和其他垃圾字符串。解决方案是让field
标记的规则返回单元素列表,并使用mapcan
包中的cl
将这些单元素列表连接在一起。像nil
这样的垃圾元素和空白字符串只与_
规则匹配,因此它们会转换为空列表并从结果中消失。
我将变换器写成递归函数,但为了实现稳健性,您可以轻松地将其拆分为仅匹配(Object ...)
性别的顶级转换器,以及仅与{{{{}}匹配的单独转换器。 1}}性别。