在Clojure生态中,clojure.data.xml是官方维护的XML处理库,提供了简洁的API用于XML的解析、生成和转换,相比其他第三方XML库,它的设计更符合Clojure的函数式编程范式,使用起来更加灵活。

环境准备
首先需要在项目的依赖配置中添加clojure.data.xml的依赖,如果你使用Leiningen,在project.clj中添加如下配置:
;; project.clj 依赖配置
:dependencies [[org.clojure/clojure "1.11.1"]
[org.clojure/data.xml "0.2.0"]]
如果是使用deps.edn管理依赖,添加如下内容:
{:deps {org.clojure/clojure {:mvn/version "1.11.1"}
org.clojure/data.xml {:mvn/version "0.2.0"}}}
XML解析基础
clojure.data.xml提供了parse函数用于解析XML输入,支持从字符串、文件、输入流等多种数据源解析XML。
解析XML字符串
下面的示例演示如何解析一个简单的XML字符串:
(require '[clojure.data.xml :as xml])
;; 定义待解析的XML字符串
(def xml-str "<?xml version="1.0" encoding="UTF-8"?>
<user>
<id>1001</id>
<name>张三</name>
<age>25</age>
<hobbies>
<hobby>阅读</hobby>
<hobby>跑步</hobby>
</hobbies>
</user>")
;; 解析XML字符串,parse-str是解析字符串的便捷函数
(def parsed-xml (xml/parse-str xml-str))
parsed-xml
解析后的结果是一个Clojure数据结构,根节点是:user,包含子节点:id、:name等,每个节点都是clojure.data.xml.Element类型的实例,包含标签名、属性和子节点信息。
解析XML文件
如果需要解析本地XML文件,可以使用parse函数配合java.io.FileReader:
(require '[clojure.java.io :as io]) ;; 解析当前目录下的user.xml文件 (def xml-file (io/file "user.xml")) (def parsed-file-xml (xml/parse (io/reader xml-file)))
解析结果的操作
解析得到的XML结构本质是一个嵌套的Clojure数据,我们可以通过常规的集合操作来提取需要的内容。
提取节点内容
使用xml/content函数可以获取节点的文本内容,结合xml/element相关的选择函数可以定位到目标节点:
(require '[clojure.data.xml :as xml])
;; 获取user节点的name子节点的内容
(def user-name (-> parsed-xml
(xml/element :name) ;; 查找第一个name子元素
xml/content ;; 获取该元素的内容
first)) ;; 内容返回的是序列,取第一个元素
(println "用户名:" user-name) ;; 输出:用户名:张三
;; 获取所有hobby节点的内容
(def hobbies (->> parsed-xml
(xml/element :hobbies) ;; 找到hobbies节点
xml/content ;; 获取hobbies的子节点序列
(filter #(= :hobby (:tag %))) ;; 筛选出标签为hobby的节点
(map #(first (xml/content %))))) ;; 提取每个hobby节点的内容
(println "爱好:" hobbies) ;; 输出:爱好:(阅读 跑步)
处理节点属性
如果XML节点包含属性,可以通过:attrs键获取属性映射:
;; 假设XML节点包含属性,比如 <user id="1001" type="vip"> (def attr-xml-str "<user id="1001" type="vip"><name>李四</name></user>") (def attr-parsed (xml/parse-str attr-xml-str)) ;; 获取user节点的id属性 (def user-id (-> attr-parsed :attrs :id)) (println "用户ID:" user-id) ;; 输出:用户ID:1001
生成XML
clojure.data.xml同样支持从Clojure数据结构生成XML字符串,核心函数是element和emit-str。
构建简单XML
下面的示例演示如何构建一个简单的用户XML:
(require '[clojure.data.xml :as xml])
;; 构建user元素,包含id、name、age子元素
(def user-xml (xml/element :user {}
(xml/element :id {} "1002")
(xml/element :name {} "王五")
(xml/element :age {} "30")))
;; 将元素转换为XML字符串
(def xml-output (xml/emit-str user-xml))
(println xml-output)
;; 输出:<?xml version="1.0" encoding="UTF-8"?><user><id>1002</id><name>王五</name><age>30</age></user>
构建嵌套XML
构建嵌套结构的XML只需要嵌套调用xml/element函数即可:
;; 构建包含嵌套hobbies节点的XML
(def nested-xml (xml/element :user {}
(xml/element :id {} "1003")
(xml/element :name {} "赵六")
(xml/element :hobbies {}
(xml/element :hobby {} "游泳")
(xml/element :hobby {} "绘画"))))
(println (xml/emit-str nested-xml))
;; 输出:<?xml version="1.0" encoding="UTF-8"?><user><id>1003</id><name>赵六</name><hobbies><hobby>游泳</hobby><hobby>绘画</hobby></hobbies></user>
添加XML属性
构建元素时,第二个参数就是属性映射,可以传入需要的属性:
;; 构建带属性的XML节点
(def attr-xml (xml/element :user {:id "1004" :type "normal"}
(xml/element :name {} "孙七")))
(println (xml/emit-str attr-xml))
;; 输出:<?xml version="1.0" encoding="UTF-8"?><user id="1004" type="normal"><name>孙七</name></user>
常见注意事项
- 解析XML时,如果XML包含命名空间,解析后的节点标签会包含命名空间信息,处理时需要注意匹配完整的标签名。
- 生成XML时,
emit-str默认会添加XML声明头,如果不需要可以自定义输出逻辑,或者使用emit函数输出到指定的输出流。 - 处理大XML文件时,建议使用
xml/parse的流式解析能力,避免一次性将整个XML加载到内存中导致内存溢出。 - 所有XML中的特殊字符在构建XML时不需要手动转义,clojure.data.xml会自动处理内容的转义工作。
clojure.data.xmlClojureXML解析XML生成修改时间:2026-06-27 05:45:39