一篇文章了解XML
一、什么是 XML?
XML 是一种结构化数据的标记语言,用来存储、传输和描述数据。
它和 HTML 很像,但它的标签是自定义的,不限定格式和外观,而是强调数据的结构和含义。
XML不是用来展示数据的,HTML是用来展示数据的。
示例:
<student><name>小明</name><age>20</age><major>计算机</major>
</student>
它表示一个学生,包含姓名、年龄和专业。这就叫结构化数据。
二、XML的作用
XML 数据格式最主要的功能就是:数据传输
XML 数据格式主要的用途又有哪些?
- 程序之间的数据传输通讯
- 配置文件 config.xml
- 存储数据,充当小型数据库 data.xml
规范数据格式,是数据具有结构性,易读易处理。
场景 | 说明 |
---|---|
配置文件 | 如 Spring 的 applicationContext.xml 、MyBatis 配置 |
数据存储 | 可用作小型数据库、日志、缓存数据 |
网络传输 | WebService(SOAP)以前就是基于 XML |
数据交换 | 不同系统之间用 XML 传数据(如银行系统) |
三、XML 的语法规则
基本结构:
<?xml version="1.0" encoding="UTF-8"?> <!-- XML 声明,非必需但推荐写 -->
<root><element>内容</element>
</root>
XML 的 6 个语法规则:
规则 | 说明 | 示例 |
---|---|---|
必须有根元素 | 整个文档必须有且只有一个根节点 | <root>...</root> |
标签成对出现 | 开始标签和结束标签必须配对 | <name>Tom</name> |
标签区分大小写 | <Name> ≠ <name> | 严格匹配 |
属性必须加引号 | 属性值必须用引号包裹 | <person gender="male"/> |
空标签可以简写 | 没有内容的标签可以自闭合 | <br /> |
不能有非法字符 | 如 < , & 不能直接出现在内容中 | 用 < , & |
XML 和 HTML 有哪些不一样?
1:HTML 标签不能自定义,XML 标签只能自定义;
2:HTML 语法要求不严格;XML 语法要求极其严格,必须是成对标签
3:xml 用来传输和存储数据,HTML 用来展示数据;
四、元素、属性、注释
4-1、元素(Element):
<book><title>XML 入门</title>
</book>
元素用于包裹信息,有开始和结束标签。
4-2、属性(Attribute):
<book title="XML 入门" price="59.8" />
属性是附加在标签上的键值对,用来表示额外信息。
属性的命名规则:
数字、字母、下划线;数字不能开头!
一般建议:数据用子元素,元数据用属性。
概念 | 解释 | 举例 |
---|---|---|
数据(Data) | 你真正关心的业务内容 | 姓名、年龄、价格、标题等 |
元数据(Metadata) | 对数据的描述或附加说明,不是数据本身 | id、类型、单位、语言、版本等 |
4-3、注释:
<!-- 这是一本书 -->
<book>...</book>
和 HTML 一样,注释用 <!-- -->
。
五、特殊字符使用实体转义
字符 | 实体写法 |
---|---|
< | < |
> | > |
& | & |
" | " |
' | ' |
六、CDATA
在 XML 中使用
<![CDATA[...]]>
是为了处理包含特殊字符或需要保留原始格式的内容。当你想在 XML 中保留“原样文本”,而不想让里面的字符被当作标签或实体解析时,就用 CDATA。
CDATA(不转义内容):
<![CDATA[ 你的原始文本 ]]>
⚠️ 限制:
-
不能嵌套 CDATA(你不能在 CDATA 里面再写
<![CDATA[
) -
不能包含
]]>
本身,否则解析器会当 CDATA 提前结束 -
CDATA 是 节点的文本内容,不能用于属性值(属性值必须转义)
示例:文本中有 <
,不能直接写
<message>2 < 5</message> <!-- ❌ 错误写法,会报错 -->
<message>2 < 5</message> <!-- ✅ 正确,但繁琐 -->
更好的写法:
<message><![CDATA[2 < 5]]></message>
七、DTD / XSD
格式正确的XML(Well Formed)是指XML的格式是正确的,可以被解析器正常读取。
而合法的XML是指,不但XML格式正确,而且它的数据结构可以被DTD或者XSD验证。
DTD文档可以指定一系列规则,例如:
- 根元素必须是
book
book
元素必须包含name
,author
等指定元素isbn
元素必须包含属性lang
- ...
如何验证XML文件的正确性呢?最简单的方式是通过浏览器验证。可以直接把XML文件拖拽到浏览器窗口,如果格式错误,浏览器会报错。
和结构类似的HTML不同,浏览器对HTML有一定的“容错性”,缺少关闭标签也可以被解析,但XML要求严格的格式,任何没有正确嵌套的标签都会导致错误。
XML是一个技术体系,除了我们经常用到的XML文档本身外,XML还支持:
特性 | 作用 | 举例用途 |
---|---|---|
✅ DTD / XSD | 验证 XML 结构是否合法 | 校验配置、数据格式 |
✅ XPath | 精确查找 XML 节点 | XML 查询、筛选数据 |
✅ XSLT | 转换 XML → HTML、XML、文本等 | 报表生成、网页渲染 |
✅ DOM / SAX | 解析 XML 文件(Java 常用) | 读写 XML 数据文件 |
✅ JAXB | Java 类 ↔ XML 的自动映射 | 配置加载、数据传输 |
实际上,XML的这些相关技术实现起来非常复杂,在实际应用中很少用到,通常了解一下就可以了。
八、XML 高级特性
总览:XML 的五大高级特性
特性 | 作用 | 举例用途 |
---|---|---|
✅ DTD / XSD | 验证 XML 结构是否合法 | 校验配置、数据格式 |
✅ XPath | 精确查找 XML 节点 | XML 查询、筛选数据 |
✅ XSLT | 转换 XML → HTML、XML、文本等 | 报表生成、网页渲染 |
✅ DOM / SAX | 解析 XML 文件(Java 常用) | 读写 XML 数据文件 |
✅ JAXB | Java 类 ↔ XML 的自动映射 | 配置加载、数据传输 |
8-1、DTD / XSD:验证 XML 的结构(结构约束)
为什么需要它?
你写了一个 XML 文档,比如:
<person><name>张三</name><age>20</age>
</person>
但你怎么知道:
-
有没有多写/漏写标签?
-
类型对不对?
-
标签顺序是否正确?
就需要用 DTD(Document Type Definition)或 XSD(XML Schema) 来定义**“这份 XML 应该长什么样”**。
1、DTD 示例:
<!DOCTYPE person [<!ELEMENT person (name, age)><!ELEMENT name (#PCDATA)><!ELEMENT age (#PCDATA)>
]>
用于说明:
-
person
标签下有name
和age
-
内容是文本
2、XSD 示例(更强大,支持类型):
<xs:element name="age" type="xs:integer"/>
XSD 支持:
-
数字、字符串、日期等类型
-
可选/必填字段
-
枚举值、正则表达式
8-2、XPath:XML 中的路径语言
类似 HTML 中的 CSS Selector
如果你有这样一份 XML:
<students><student><name>小明</name><age>20</age></student>
</students>
你可以用 XPath 表达式精准查找:
XPath | 含义 | 返回内容 |
---|---|---|
/students/student/name | 根节点开始查找 name | 小明 |
//name | 所有叫 name 的元素 | 小明 |
//student[age>18] | 年龄大于 18 的学生 | student 节点 |
XPath 广泛用于:XML 查询引擎、XSLT 过滤器、Selenium 网页爬虫等
8-3、XSLT:XML 转换工具
把一份 XML 转换成另一份 XML、HTML 或纯文本,像是“模板引擎”。
应用场景:
-
把 XML 转为 HTML 页面(用于新闻站点、商品列表等)
-
把数据格式从 A 转为 B(跨平台转换)
-
生成报表、文档
示例:把 XML 转成 HTML 列表
原始 XML:
<books><book><title>Java基础</title><price>59.8</price></book>
</books>
XSLT 文件:
<xsl:template match="book"><li><xsl:value-of select="title"/> - ¥<xsl:value-of select="price"/></li>
</xsl:template>
最终生成:
<li>Java基础 - ¥59.8</li>
8-4、DOM / SAX:XML 文件解析方式(Java 中常见)
因为XML是一种树形结构的文档,它有两种标准的解析API:
- DOM:一次性读取XML,并在内存中表示为树形结构;
- SAX:以流的形式读取XML,使用事件回调。
1、DOM(Document Object Model)
-
一次性读取整棵 XML 树结构进内存
-
优点:操作灵活、结构清晰
-
缺点:大文件占内存高
DOM模型就是把XML结构作为一个树形结构处理,从根节点开始,每个节点都可以包含任意个子节点。
示例:
<?xml version="1.0" encoding="UTF-8" ?>
<book id="1"><name>Java核心技术</name><author>Cay S. Horstmann</author><isbn lang="CN">1234567</isbn><tags><tag>Java</tag><tag>Network</tag></tags><pubDate/>
</book>
注意到最顶层的document代表XML文档,它是真正的“根”,而<book>
虽然是根元素,但它是document
的一个子节点。
Java提供了DOM API来解析XML,它使用下面的对象来表示XML的内容:
- Document:代表整个XML文档;
- Element:代表一个XML元素;
- Attribute:代表一个元素的某个属性。
使用DOM API解析一个XML文档的代码如下:
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
Document doc = factory.newDocumentBuilder().parse("file.xml");
Element root = doc.getDocumentElement();
或者:
InputStream input = Main.class.getResourceAsStream("/book.xml");
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(input);
DocumentBuilder.parse()
用于解析一个XML,它可以接收InputStream,File或者URL,如果解析无误,我们将获得一个Document对象,这个对象代表了整个XML文档的树形结构,需要遍历以便读取指定元素的值:
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;public class XmlPrinter {public static void main(String[] args) throws Exception {// 1. 加载 XML 文件File file = new File("/Users/wangsi/IdeaProjects/javaSE/src/student.xml"); // 文件路径DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();DocumentBuilder builder = factory.newDocumentBuilder();// 2. 解析为 Document 对象Document document = builder.parse(file);// 3. 获取根节点(Element 是 Node 的子类)Node root = document.getDocumentElement();// 4. 调用你写的递归打印方法printNode(root, 0);}public static void printNode(Node n, int indent) {for (int i = 0; i < indent; i++) {System.out.print(' ');}switch (n.getNodeType()) {case Node.DOCUMENT_NODE:System.out.println("Document: " + n.getNodeName());break;case Node.ELEMENT_NODE:System.out.println("Element: " + n.getNodeName());break;case Node.TEXT_NODE:// 忽略只包含空白的文本节点String text = n.getNodeValue().trim();if (!text.isEmpty()) {System.out.println("Text: " + n.getNodeName() + " = " + text);}break;case Node.ATTRIBUTE_NODE:System.out.println("Attr: " + n.getNodeName() + " = " + n.getNodeValue());break;default:System.out.println("NodeType: " + n.getNodeType() + ", NodeName: " + n.getNodeName());}// 5. 如果是元素,还要打印属性if (n.getNodeType() == Node.ELEMENT_NODE) {NamedNodeMap attrs = n.getAttributes();for (int i = 0; i < attrs.getLength(); i++) {printNode(attrs.item(i), indent + 2);}}// 6. 遍历子节点for (Node child = n.getFirstChild(); child != null; child = child.getNextSibling()) {printNode(child, indent + 2);}}}
对于DOM API解析出来的结构,我们从根节点Document出发,可以遍历所有子节点,获取所有元素、属性、文本数据,还可以包括注释,这些节点被统称为Node,每个Node都有自己的Type,根据Type来区分一个Node到底是元素,还是属性,还是文本,等等。
使用DOM API时,如果要读取某个元素的文本,需要访问它的Text类型的子节点,所以使用起来还是比较繁琐的。
使用DOM解析XML的优点是用起来省事,但它的主要缺点是内存占用太大。
2、SAX(Simple API for XML)
-
事件驱动式解析,不存整个树结构,边读边处理
-
适合大文件或只读操作
SAX是Simple API for XML的缩写,它是一种基于流的解析方式,边读取XML边解析,并以事件回调的方式让调用者获取数据。因为是一边读一边解析,所以无论XML有多大,占用的内存都很小。
SAX解析会触发一系列事件:
- startDocument:开始读取XML文档;
- startElement:读取到了一个元素,例如
<book>
; - characters:读取到了字符;
- endElement:读取到了一个结束的元素,例如
</book>
; - endDocument:读取XML文档结束。
如果我们用SAX API解析XML,Java代码如下:
InputStream input = Main.class.getResourceAsStream("/book.xml");
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser saxParser = spf.newSAXParser();
saxParser.parse(input, new MyHandler());
关键代码SAXParser.parse()
除了需要传入一个InputStream
外,还需要传入一个回调对象,这个对象要继承自DefaultHandler:
class MyHandler extends DefaultHandler {public void startDocument() throws SAXException {print("start document");}public void endDocument() throws SAXException {print("end document");}public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {print("start element:", localName, qName);}public void endElement(String uri, String localName, String qName) throws SAXException {print("end element:", localName, qName);}public void characters(char[] ch, int start, int length) throws SAXException {print("characters:", new String(ch, start, length));}public void error(SAXParseException e) throws SAXException {print("error:", e);}void print(Object... objs) {for (Object obj : objs) {System.out.print(obj);System.out.print(" ");}System.out.println();}
}
运行SAX解析代码,可以打印出下面的结果:
start document
start element: book
characters:start element: name
characters: Java核心技术
end element: name
characters:start element: author
...
DOM 更适合增删查改,SAX 更适合读文件做分析。
8-5、JAXB:Java ↔ XML 映射工具(非常实用!)
JAXB(Java Architecture for XML Binding)可以把 XML 文件自动映射成 Java 对象,或者反过来写入 XML。
示例:
Java Bean:
@XmlRootElement(name = "person")
public class Person {public String name;public int age;
}
读取 XML 到对象:
JAXBContext context = JAXBContext.newInstance(Person.class);
Person p = (Person) context.createUnmarshaller().unmarshal(new File("person.xml"));
类似 JSON ↔ Java(Gson、Jackson),但是 XML 的双向转换。
8-6、使用Jackson:Java ↔ XML 映射工具
Jackson 和 JAXB,两者都能实现“XML ↔ Java 对象”的双向转换。
- Jackson 更通用,支持 JSON 和 XML;
- JAXB 是专门为 XML 设计的官方标准。
特性 | JAXB | Jackson |
---|---|---|
官方地位 | Java 官方标准 | 第三方库 |
XML 支持 | 原生、强大 | 通过扩展模块 |
JSON 支持 | ❌ 不支持 | ✅ 支持(主打) |
默认注解包 | javax.xml.bind.annotation.* | com.fasterxml.jackson.* |
是否支持 Schema | ✅ 可通过 XSD 生成 Java 类 | ❌ 不擅长 |
Java 版本 | Java 8 自带,Java 11 需要手动导包 | 全版本支持 |
在 Spring 中的使用 | 较少用 | ✅ Spring Boot 默认 JSON/XML 工具 |
前面我们介绍了DOM和SAX两种解析XML的标准接口。但是,无论是DOM还是SAX,使用起来都不直观。
如果能直接从XML文档解析成一个JavaBean,那比DOM或者SAX不知道容易到哪里去了。
一个名叫Jackson的开源的第三方库可以轻松做到XML到JavaBean的转换。我们要使用Jackson,先添加一个Maven的依赖。
<dependency><groupId>com.fasterxml.jackson.dataformat</groupId><artifactId>jackson-dataformat-xml</artifactId><version>2.15.3</version> <!-- 用最新版 -->
</dependency>
Jackson 支持两种方式协同工作:
-
使用
XmlMapper
-
配合注解,来精细控制 XML ↔ JavaBean 的映射关系
1、 使用 XmlMapper
示例:
InputStream input = Main.class.getResourceAsStream("/book.xml");
JacksonXmlModule module = new JacksonXmlModule();
XmlMapper mapper = new XmlMapper(module);
Book book = mapper.readValue(input, Book.class);
System.out.println(book.id);
System.out.println(book.name);
System.out.println(book.author);
System.out.println(book.isbn);
System.out.println(book.tags);
System.out.println(book.pubDate);
注意到XmlMapper
就是我们需要创建的核心对象,可以用readValue(InputStream, Class)
直接读取XML并返回一个JavaBean。运行上述代码,就可以直接从Book对象中拿到数据。
2、配合注解,精细控制
示例:
@JacksonXmlRootElement(localName = "Person")
public class Person {@JacksonXmlProperty(localName = "name")private String name;@JacksonXmlProperty(isAttribute = true)private int age;
}
方式 | 你告诉它 | 例子 |
---|---|---|
默认方式 | 靠字段名自动猜 | XmlMapper.readValue(input, Book.class) |
注解方式 | 你明确告诉它 “name 是元素”、“age 是属性” | @JacksonXmlProperty(...) |
当你要做以下事情时,注解就必须上场了:
场景 | 用法 |
---|---|
XML 字段名 和 Java 字段名 不一样 | @JacksonXmlProperty(localName = "bookName") |
要把字段转成属性而不是子标签 | @JacksonXmlProperty(isAttribute = true) |
需要根标签重命名 | @JacksonXmlRootElement(localName = "Book") |
嵌套结构/列表 | @JacksonXmlElementWrapper(useWrapping = false) 等 |
3、Spring Boot 默认集成 Jackson
Spring Boot 默认集成 Jackson,自动将 Java 对象 和 JSON 之间进行转换,主要用于:
-
请求参数(@RequestBody)
-
响应数据(@ResponseBody / REST 接口)
Spring Boot 使用 Jackson 的核心场景是:
1. 控制器返回对象 → 自动转为 JSON:
@RestController
public class BookController {@GetMapping("/book")public Book getBook() {return new Book("Java入门", "张三");}
}
访问 /book
时,Spring Boot 自动把 Book
对象转为 JSON:
{"title": "Java入门","author": "张三"
}
2. 接收 JSON 请求体并自动转为 Java 对象:
@PostMapping("/book")
public String addBook(@RequestBody Book book) {return "收到书:" + book.getTitle();
}
如果前端传:
{"title": "Java核心","author": "李四"
}
后端会自动把它转换为 Java 对象。