一、什么是 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 對象。