面试题手册

梳理高频技术问题,帮助你按主题复习和查漏补缺。

计算机基础阅读 02026年5月27日 10:50

XML 中的 CDATA 是什么?什么时候需要用 CDATA?

CDATA(Character Data)是 XML 里的一个特殊标记,告诉解析器"这段内容别解析,原样保留"。当你需要在 XML 中放代码、HTML 片段或包含大量 <、>、& 的文本时,CDATA 省去逐个转义的麻烦。基本语法<code> <![CDATA[ if (x < 10 && y > 5) { return "ok"; } ]]></code><![CDATA[ 和 ]]> 之间的内容,XML 解析器不会尝试解析标签或实体引用,全部当作原始文本处理。什么时候需要 CDATA嵌入代码:JavaScript、SQL、CSS 里大量使用 <、>、&&,不用 CDATA 就得写成 <、>、&&,可读性极差。<script> <![CDATA[ function check() { if (count < 10 && status === "active") { return true; } } ]]></script>嵌入 HTML 片段:RSS feed 里经常包含 HTML 内容,CDATA 是标准做法。<description> <![CDATA[ <p>这是一段<strong>HTML</strong>内容</p> ]]></description>嵌入 SQL 查询:MyBatis、Hibernate 的 XML 映射文件里写 SQL,比较运算符必须转义或用 CDATA。<select id="findActive"> <![CDATA[ SELECT * FROM users WHERE age > 18 AND score >= 60 ]]></select>CDATA 的限制不能嵌套:CDATA 内部不能出现 ]]>,因为解析器会把第一个 ]]> 当作 CDATA 结束标记。如果内容里确实需要 ]]>,得拆成两个 CDATA 节:]]]><![CDATA[>。大小写敏感:必须是 CDATA,写成 cdata 或 Cdata 都不对。空白保留:CDATA 里的换行和缩进会原样保留,包括你不想保留的。格式化 XML 时注意别误改 CDATA 内的空白。不能做部分转义:CDATA 是全有或全无的——整个内容都不解析。如果只需要转义个别字符,用实体引用 < > 更精确。CDATA vs 实体引用| 特性 | CDATA | 实体引用 ||------|-------|----------|| 语法 | <![CDATA[...]]> | < > & || 适用范围 | 大段文本 | 单个字符 || 可读性 | 高,原文可读 | 低,需要还原 || 灵活性 | 低,整个块不解析 | 高,精确控制 |经验法则:超过 3 个特殊字符就用 CDATA,少于 3 个用实体引用。常见误区CDATA 不是数据类型:CDATA 只是告诉解析器别解析,它不改变数据的含义。解析后 <![CDATA[hello]]> 和 hello 是等价的——应用程序拿到的是同样的字符串。CDATA 不影响验证:XSD 验证时,CDATA 内的内容同样会被检查是否符合类型约束。CDATA 只跳过解析,不跳过验证。浏览器中的 CDATA:XHTML 里曾经用 //<![CDATA[ 包裹 JavaScript,但 HTML5 不需要——<script> 标签的内容本身就不被当作 XML 解析。这个用法已经过时了。
计算机基础阅读 02026年5月27日 10:49

XML 和 JSON 有什么区别?什么时候该用 XML?

XML 和 JSON 是两种最常用的数据交换格式,但它们的定位不同:XML 是标记语言,擅长表达文档结构;JSON 是数据格式,擅长表达结构化数据。现代 Web 开发 90% 的场景用 JSON,但 XML 在特定领域仍然不可替代。核心区别| 特性 | XML | JSON ||------|-----|------|| 定位 | 标记语言,面向文档 | 数据格式,面向数据 || 语法 | 标签闭合 <name>值</name> | 键值对 "name": "值" || 数据类型 | 无内置类型,都是字符串 | string、number、boolean、null、array、object || 注释 | 支持 <!-- --> | 不支持 || 命名空间 | 支持,避免标签冲突 | 不支持 || 验证 | DTD / XSD 成熟方案 | JSON Schema(较新,工具链不完善) || 冗余度 | 高(开闭标签重复) | 低 || 解析速度 | 慢(DOM/SAX) | 快(原生支持) |一句话概括:XML 能做文档,JSON 只能做数据;JSON 传输快,XML 验证强。什么时候必须用 XML包含文档内容:XML 的标签能表达语义和层级(标题、段落、列表),JSON 的键值对做不到。Office 文档(docx、xlsx)底层是 XML,RSS/Atom feed 也是 XML——这些场景需要混合内容和结构。需要严格验证:XSD 可以定义精确的类型约束(值范围、正则模式、枚举),JSON Schema 功能弱得多。金融、医疗、政府数据交换标准(如 HL7、FHIR 的 XML 格式)依赖 XSD 验证。命名空间:多个词汇表组合时,命名空间避免标签冲突。SOAP、XHTML、SVG 都用命名空间。JSON 没有这个能力。遗留系统集成:企业里大量旧系统只认 XML。SAP、Oracle、银行接口——你不想用也得用。JSON 的优势场景Web API:RESTful API 用 JSON 是事实标准。体积小、解析快、前端原生支持,没有理由用 XML。配置文件:package.json、tsconfig.json、.eslintrc——开发工具链已经全面倒向 JSON(以及 JSON 超集如 JSON5、YAML)。移动端和低带宽场景:JSON 比 XML 小 30-50%,解析快 2-3 倍。移动网络下这个差距很实际。NoSQL 数据库:MongoDB、CouchDB 存储 JSON 文档,查询天然适配。同一数据的格式对比<!-- XML --><book id="1" category="web"> <title>XML Guide</title> <price>39.95</price> <tags> <tag>XML</tag> <tag>Programming</tag> </tags></book>{ "id": 1, "category": "web", "title": "XML Guide", "price": 39.95, "tags": ["XML", "Programming"]}XML 版本 156 字节,JSON 版本 98 字节。数据量大的时候,这个差距更明显。格式互转XML 和 JSON 互转不是无损的——XML 的属性、命名空间、混合内容在 JSON 里没有对应概念:XML 属性(<book id="1">)转 JSON 时变成普通字段,丢失"这是属性"的语义XML 混合内容(<p>文字<b>加粗</b>继续</p>)转 JSON 需要特殊处理JSON 的数组类型转 XML 时只能用重复标签模拟所以别指望"先写 XML 再转 JSON"或反过来——两种格式的数据模型不同,互转会丢信息。选型决策简单判断:如果数据是给人读的文档,用 XML;如果是给程序消费的结构化数据,用 JSON。如果两者都涉及(比如带格式的富文本数据),考虑用 JSON 做传输、XML 做存储,或者直接用 Markdown + JSON 元数据的组合方案。
计算机基础阅读 02026年5月27日 10:49

XML Schema 和 DTD 有什么区别?XSD 为什么取代了 DTD?

XML Schema(XSD)和 DTD 都用来定义 XML 文档的结构和约束,但能力差距很大。XSD 是 DTD 的现代替代方案——基于 XML 语法、支持数据类型、支持命名空间、可扩展可继承。DTD 语法简单但功能有限,新项目几乎不再使用。核心区别| 特性 | XML Schema (XSD) | DTD ||------|------------------|-----|| 语法 | XML 格式,可用 XML 解析器处理 | 自有语法,不是 XML || 数据类型 | 丰富内置类型(string、int、date、boolean 等) | 只有字符串,没有类型区分 || 命名空间 | 原生支持 | 不支持 || 类型继承 | 支持 extension 和 restriction | 不支持 || 可重用性 | 支持类型导入和引用 | 难以复用 || 约束精度 | 可定义值范围、正则模式、枚举 | 只能定义元素出现次数 |简单说:XSD 能做的 DTD 做不了(类型约束、命名空间),DTD 能做的 XSD 都能做且做得更好。XSD 基础结构XSD 本身是 XML 文档,根元素是 <xs:schema>:<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="book" type="BookType"/> <xs:complexType name="BookType"> <xs:sequence> <xs:element name="title" type="xs:string"/> <xs:element name="price" type="xs:decimal"/> <xs:element name="publishDate" type="xs:date"/> </xs:sequence> <xs:attribute name="id" type="xs:string" use="required"/> </xs:complexType></xs:schema>complexType 定义包含子元素或属性的复杂类型,simpleType 定义带约束的简单类型。XSD 的约束能力XSD 比 DTD 强的地方在于精确约束:<!-- 值范围约束 --><xs:simpleType name="AgeType"> <xs:restriction base="xs:integer"> <xs:minInclusive value="0"/> <xs:maxInclusive value="120"/> </xs:restriction></xs:simpleType><!-- 正则约束 --><xs:simpleType name="EmailType"> <xs:restriction base="xs:string"> <xs:pattern value="[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}"/> </xs:restriction></xs:simpleType><!-- 枚举约束 --><xs:simpleType name="StatusType"> <xs:restriction base="xs:string"> <xs:enumeration value="active"/> <xs:enumeration value="inactive"/> </xs:restriction></xs:simpleType>DTD 只能声明元素存在和出现次数,无法约束值的格式和范围。类型继承和扩展XSD 支持两种继承方式:extension:在基础类型上添加新元素或属性restriction:在基础类型上收紧约束<!-- 扩展:在 PersonType 上加 department --><xs:complexType name="EmployeeType"> <xs:complexContent> <xs:extension base="PersonType"> <xs:sequence> <xs:element name="department" type="xs:string"/> </xs:sequence> </xs:extension> </xs:complexContent></xs:complexType>这是 DTD 完全做不到的——DTD 没有类型体系,每个元素定义都是独立的。在 XML 中引用 XSD<book xmlns="http://www.example.com/books" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.example.com/books books.xsd"> <title>XML Guide</title> <price>49.99</price> <publishDate>2024-01-15</publishDate></book>schemaLocation 属性成对出现:命名空间 URI + XSD 文件路径。解析器会根据 XSD 验证文档内容。什么时候还在用 DTD?DTD 在 2025 年基本只剩这些场景:维护遗留系统(改 DTD 风险比替换小)HTML5 的 DOCTYPE 声明(严格说是简化版 DTD)简单的配置文件验证(不值得写 XSD 的场景)新项目用 XSD,没有理由选 DTD。如果嫌 XSD 太啰嗦,可以考虑 RelaxNG——更简洁的替代方案。
计算机基础阅读 02月21日 14:23

什么是 XML 命名空间,如何声明和使用它?

XML 命名空间(Namespace)是 XML 中用于解决元素和属性名称冲突的机制。当多个 XML 文档或架构合并时,可能会出现相同名称的元素代表不同含义的情况,命名空间通过为元素和属性添加唯一标识符来解决这个问题。命名空间的声明命名空间使用 xmlns 属性声明,语法格式为:<root xmlns:prefix="namespaceURI"> <prefix:element>内容</prefix:element></root>其中:xmlns 是保留属性,用于声明命名空间prefix 是命名空间前缀(可选,默认命名空间不需要前缀)namespaceURI 是命名空间的唯一标识符(通常是 URL)命名空间的类型1. 默认命名空间<root xmlns="http://example.com/ns"> <element>内容</element></root>默认命名空间应用于当前元素及其所有未加前缀的子元素。2. 带前缀的命名空间<root xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:schema>...</xs:schema></root>带前缀的命名空间只应用于使用该前缀的元素和属性。命名空间的作用域命名空间声明在声明它的元素及其所有后代元素中有效子元素可以覆盖父元素的命名空间声明未声明命名空间的元素属于"无命名空间"命名空间的最佳实践使用唯一的 URI:命名空间 URI 应该是唯一的,通常使用 URL 格式选择有意义的前缀:前缀应该简短且易于理解避免过度使用:只在必要时使用命名空间保持一致性:在整个文档中使用相同的命名空间声明实际应用示例<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:m="http://www.example.com/stock"> <soap:Header> <m:Authentication> <m:Username>user</m:Username> <m:Password>pass</m:Password> </m:Authentication> </soap:Header> <soap:Body> <m:GetStockPrice> <m:StockSymbol>IBM</m:StockSymbol> </m:GetStockPrice> </soap:Body></soap:Envelope>在这个例子中,soap 前缀用于 SOAP 协议的元素,m 前缀用于自定义的业务逻辑元素,两者互不干扰。
计算机基础阅读 02月21日 14:22

什么是 XML 实体,有哪些类型以及如何使用它们?

XML 实体(Entity)是一种用于定义可重用内容的机制,它允许在 XML 文档中定义一次,然后在多个地方引用。实体可以提高 XML 文档的可维护性和可读性。XML 实体的类型1. 内部实体内部实体在 DTD 中定义,其值直接包含在 DTD 中。<!DOCTYPE root [ <!ENTITY company "ABC Corporation"> <!ENTITY copyright "Copyright © 2024 ABC Corporation">]><root> <name>&company;</name> <footer>&copyright;</footer></root>2. 外部实体外部实体引用外部文件中的内容。<!DOCTYPE root [ <!ENTITY header SYSTEM "header.xml"> <!ENTITY footer SYSTEM "footer.xml">]><root> &header; <content>Main content here</content> &footer;</root>3. 参数实体参数实体主要用于 DTD 中,以 % 开头。<!DOCTYPE root [ <!ENTITY % commonElements " <!ELEMENT name (#PCDATA)> <!ELEMENT email (#PCDATA)> "> %commonElements;]>4. 预定义实体XML 定义了 5 个预定义实体:| 实体 | 字符 | 描述 ||------|------|------|| < | < | 小于号 || > | > | 大于号 || & | & | 和号 || ' | ' | 单引号 || " | " | 双引号 |<data> <comparison>5 < 10</comparison> <quote>She said "Hello"</quote> <ampersand>A & B</ampersand></data>实体的定义和使用内部实体示例<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE letter [ <!ENTITY sender "John Doe"> <!ENTITY recipient "Jane Smith"> <!ENTITY greeting "Dear"> <!ENTITY closing "Sincerely">]><letter> <salutation>&greeting; &recipient;,</salutation> <body> This is a sample letter from &sender; to &recipient;. </body> <signature>&closing;, &sender;</signature></letter>外部实体示例<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE book [ <!ENTITY chapter1 SYSTEM "chapter1.xml"> <!ENTITY chapter2 SYSTEM "chapter2.xml"> <!ENTITY chapter3 SYSTEM "chapter3.xml">]><book> <title>Complete Guide</title> &chapter1; &chapter2; &chapter3;</book>chapter1.xml:<chapter id="1"> <title>Introduction</title> <content>Welcome to the guide...</content></chapter>参数实体示例<!DOCTYPE book [ <!ENTITY % bookElements " <!ELEMENT book (title, author+, chapter+)> <!ELEMENT title (#PCDATA)> <!ELEMENT author (#PCDATA)> <!ELEMENT chapter (title, content)> <!ELEMENT content (#PCDATA)> "> %bookElements;]>实体的优点代码重用:避免重复内容易于维护:修改一处即可更新所有引用模块化:可以将内容分解为可管理的部分可读性:使 XML 文档更简洁易读灵活性:可以动态替换内容实体的缺点安全性:外部实体可能带来安全风险(XXE 攻击)复杂性:增加 XML 文档的复杂性性能:解析实体可能影响性能兼容性:某些解析器可能不支持所有实体类型安全考虑XXE 攻击风险外部实体可能被滥用来读取服务器文件或发起攻击:<!-- 恶意的 XXE 攻击 --><!DOCTYPE data [ <!ENTITY xxe SYSTEM "file:///etc/passwd">]><data> <content>&xxe;</content></data>防护措施禁用外部实体 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);使用白名单 <!DOCTYPE root [ <!ENTITY % safe SYSTEM "safe.dtd"> %safe; ]>输入验证 if (xml.contains("<!ENTITY") || xml.contains("SYSTEM")) { throw new SecurityException("Potentially malicious content"); }最佳实践1. 合理使用实体<!-- 好的做法:使用实体定义常用内容 --><!DOCTYPE config [ <!ENTITY company "My Company"> <!ENTITY version "1.0.0">]><config> <application>&company; App</application> <version>&version;</version></config>2. 避免过度使用<!-- 不好的做法:过度使用实体 --><!DOCTYPE root [ <!ENTITY a "A"> <!ENTITY b "B"> <!ENTITY c "C"> <!ENTITY d "D">]><root>&a;&b;&c;&d;</root>3. 使用有意义的名称<!-- 好的做法:使用有意义的实体名称 --><!DOCTYPE letter [ <!ENTITY companyName "ABC Corporation"> <!ENTITY currentYear "2024">]><letter> <footer>&companyName; &currentYear;</footer></letter>4. 文档化实体<!-- 在 DTD 中添加注释 --><!DOCTYPE root [ <!-- Company name - used throughout the document --> <!ENTITY company "ABC Corporation"> <!-- Copyright notice - appears in footer --> <!ENTITY copyright "Copyright © 2024 ABC Corporation">]>实体在 Schema 中的替代方案使用 XML SchemaXML Schema 不支持实体,但提供了其他机制:<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="config"> <xs:complexType> <xs:sequence> <xs:element name="company" type="xs:string" fixed="ABC Corporation"/> <xs:element name="version" type="xs:string" fixed="1.0.0"/> </xs:sequence> </xs:complexType> </xs:element></xs:schema>使用 XIncludeXInclude 是 XML 的包含机制,可以替代外部实体:<book xmlns:xi="http://www.w3.org/2001/XInclude"> <title>Complete Guide</title> <xi:include href="chapter1.xml"/> <xi:include href="chapter2.xml"/> <xi:include href="chapter3.xml"/></book>总结XML 实体是一个强大的功能,可以提高 XML 文档的可维护性和可读性。然而,使用实体时需要注意安全性,特别是外部实体可能带来的 XXE 攻击风险。在现代 XML 开发中,建议:优先使用内部实体而非外部实体在生产环境中禁用外部实体使用 XML Schema 或 XInclude 作为替代方案遵循最佳实践,合理使用实体进行充分的安全测试通过正确使用 XML 实体,可以创建更清晰、更易维护的 XML 文档,同时确保应用程序的安全性。
计算机基础阅读 02月21日 14:22

在什么情况下应该使用 XML 属性而不是子元素?

XML 属性(Attributes)和子元素(Child Elements)都是用于存储数据的方式,但它们有不同的适用场景和最佳实践。XML 属性的特点优点简洁性:属性可以更简洁地表示元数据唯一性:每个元素中属性名必须唯一适合简单数据:适合存储简单的键值对数据减少嵌套:可以减少 XML 的嵌套层次缺点不能包含复杂结构:属性只能包含文本,不能包含子元素不能重复:同一元素中不能有重复的属性名难以扩展:添加新属性可能破坏现有结构没有顺序:属性没有顺序要求难以处理多值:不适合存储多值数据XML 子元素的特点优点可以包含复杂结构:子元素可以包含其他元素和属性可以重复:同一元素中可以有多个同名子元素易于扩展:可以轻松添加新的子元素有顺序:子元素有明确的顺序适合复杂数据:适合存储复杂的数据结构支持混合内容:可以包含文本和子元素的混合内容缺点冗余度高:需要更多的标签和嵌套文件较大:相比属性,会增加文件大小解析稍慢:需要更多的解析工作使用属性的场景1. 元数据信息<book id="123" isbn="978-0-123456-78-9" category="programming"> <title>XML Programming</title> <author>John Doe</author></book>2. 简单的标识符<user id="user_001" role="admin" status="active"> <name>John Doe</name> <email>john@example.com</email></user>3. 配置参数<database driver="mysql" host="localhost" port="3306" timeout="30"> <name>mydb</name> <user>root</user></database>4. 格式化选项<text format="bold" color="red" size="14"> This is important text</text>使用子元素的场景1. 复杂的数据结构<person> <name> <first>John</first> <middle>William</middle> <last>Doe</last> </name> <address> <street>123 Main St</street> <city>New York</city> <state>NY</state> <zip>10001</zip> </address></person>2. 多值数据<book> <title>XML Programming</title> <authors> <author>John Doe</author> <author>Jane Smith</author> <author>Bob Johnson</author> </authors></book>3. 长文本内容<article> <title>Introduction to XML</title> <content> XML is a markup language that defines a set of rules for encoding documents in a format that is both human-readable and machine-readable... </content></article>4. 需要顺序的数据<steps> <step>Install the software</step> <step>Configure the settings</step> <step>Run the application</step> <step>Test the functionality</step></steps>5. 混合内容<paragraph> This is <bold>important</bold> text with <italic>emphasis</italic>.</paragraph>对比示例使用属性<product id="P001" name="Laptop" price="999.99" stock="50" category="electronics"> <description>High-performance laptop</description></product>使用子元素<product> <id>P001</id> <name>Laptop</name> <price>999.99</price> <stock>50</stock> <category>electronics</category> <description>High-performance laptop</description></product>最佳实践1. 数据 vs 元数据使用属性:存储元数据(ID、类型、状态等)使用子元素:存储实际数据<book id="123" category="programming"> <title>XML Programming</title> <author>John Doe</author></book>2. 简单 vs 复杂使用属性:简单数据(单个值)使用子元素:复杂数据(结构化数据)<!-- 简单数据 --><user id="001" name="John"/><!-- 复杂数据 --><user> <id>001</id> <name> <first>John</first> <last>Doe</last> </name></user>3. 单值 vs 多值使用属性:单值数据使用子元素:多值数据<!-- 单值 --><book category="programming"/><!-- 多值 --><book> <categories> <category>programming</category> <category>reference</category> </categories></book>4. 长度考虑使用属性:短文本(通常少于 50 个字符)使用子元素:长文本<!-- 短文本 --><book isbn="978-0-123456-78-9"/><!-- 长文本 --><book> <description>This is a comprehensive guide to XML programming that covers all the essential concepts and techniques...</description></book>5. 可扩展性使用属性:相对稳定的属性使用子元素:可能变化或扩展的数据<!-- 稳定的属性 --><user id="001" role="admin"/><!-- 可能扩展的数据 --><user> <profile> <name>John Doe</name> <email>john@example.com</email> <!-- 可以轻松添加更多字段 --> </profile></user>Schema 设计建议XML Schema 示例<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="book"> <xs:complexType> <xs:sequence> <xs:element name="title" type="xs:string"/> <xs:element name="author" type="xs:string"/> <xs:element name="description" type="xs:string" minOccurs="0"/> </xs:sequence> <xs:attribute name="id" type="xs:string" use="required"/> <xs:attribute name="isbn" type="xs:string"/> <xs:attribute name="category" type="xs:string"/> </xs:complexType> </xs:element></xs:schema>总结选择使用属性还是子元素应该基于以下考虑:数据性质:元数据用属性,实际数据用子元素数据复杂度:简单数据用属性,复杂数据用子元素数据量:单值用属性,多值用子元素文本长度:短文本用属性,长文本用子元素可扩展性:稳定数据用属性,可能变化的数据用子元素可读性:考虑 XML 文档的可读性和维护性在实际应用中,通常需要结合使用属性和子元素,以获得最佳的数据表示效果。
计算机基础阅读 02月21日 14:22

什么是 XML 文档的有效性和格式良好性,它们有什么区别?

XML 文档的有效性(Validity)和格式良好性(Well-formedness)是两个重要的概念,它们定义了 XML 文档的质量标准。格式良好(Well-formed)格式良好是指 XML 文档符合 XML 语法规则,可以被 XML 解析器正确解析。格式良好的要求必须有单一的根元素 <!-- 正确:有单一根元素 --> <root> <child>内容</child> </root> <!-- 错误:没有单一根元素 --> <child1>内容</child1> <child2>内容</child2>所有标签必须正确关闭 <!-- 正确:所有标签都关闭 --> <root> <child>内容</child> </root> <!-- 错误:标签未关闭 --> <root> <child>内容 </root>标签必须正确嵌套 <!-- 正确:正确嵌套 --> <root> <parent> <child>内容</child> </parent> </root> <!-- 错误:错误嵌套 --> <root> <parent> <child>内容 </parent> </child> </root>属性值必须用引号括起来 <!-- 正确:属性值用引号括起来 --> <book id="1" category="web">内容</book> <!-- 错误:属性值未用引号括起来 --> <book id=1 category=web>内容</book>标签名称区分大小写 <!-- 正确:大小写一致 --> <root> <Child>内容</Child> </root> <!-- 错误:大小写不一致 --> <root> <child>内容</Child> </root>特殊字符必须转义 <!-- 正确:特殊字符已转义 --> <data>5 < 10 & 20 > 15</data> <!-- 错误:特殊字符未转义 --> <data>5 < 10 & 20 > 15</data>空元素必须正确表示 <!-- 正确:空元素表示方式 --> <empty/> <empty></empty> <!-- 错误:空元素表示不正确 --> <empty>有效(Valid)有效是指 XML 文档不仅格式良好,还符合特定的文档类型定义(DTD)或 XML Schema。有效的要求必须格式良好有效的文档首先必须是格式良好的必须符合 DTD 或 Schema <!-- XML 文档 --> <?xml version="1.0"?> <!DOCTYPE note SYSTEM "note.dtd"> <note> <to>John</to> <from>Jane</from> <heading>Reminder</heading> <body>Don't forget the meeting!</body> </note> <!-- DTD 文件 (note.dtd) --> <!ELEMENT note (to, from, heading, body)> <!ELEMENT to (#PCDATA)> <!ELEMENT from (#PCDATA)> <!ELEMENT heading (#PCDATA)> <!ELEMENT body (#PCDATA)>元素和属性必须符合定义 <!-- XML Schema --> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="book"> <xs:complexType> <xs:sequence> <xs:element name="title" type="xs:string"/> <xs:element name="author" type="xs:string"/> <xs:element name="price" type="xs:decimal"/> </xs:sequence> <xs:attribute name="id" type="xs:string" use="required"/> </xs:complexType> </xs:element> </xs:schema> <!-- 有效的 XML 文档 --> <book id="1"> <title>XML Guide</title> <author>John Doe</author> <price>39.95</price> </book> <!-- 无效的 XML 文档(缺少必需的属性) --> <book> <title>XML Guide</title> <author>John Doe</author> <price>39.95</price> </book>数据类型必须匹配 <!-- 正确:数据类型匹配 --> <price>39.95</price> <!-- 错误:数据类型不匹配 --> <price>thirty-nine point ninety-five</price>格式良好 vs 有效的对比| 特性 | 格式良好 | 有效 ||------|---------|------|| 语法要求 | 必须符合 XML 语法规则 | 必须符合 XML 语法规则 || 结构要求 | 必须有单一根元素 | 必须有单一根元素 || 验证要求 | 不需要 DTD 或 Schema | 需要 DTD 或 Schema || 约束检查 | 不检查约束 | 检查所有约束 || 数据类型 | 不检查数据类型 | 检查数据类型 || 必需元素 | 不检查必需元素 | 检查必需元素 || 属性约束 | 不检查属性约束 | 检查属性约束 |验证 XML 文档1. 验证格式良好性Java 示例:import javax.xml.parsers.*;import org.xml.sax.*;import org.w3c.dom.*;public class XMLValidator { public static boolean isWellFormed(String xml) { try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document document = builder.parse(new InputSource(new StringReader(xml))); return true; } catch (Exception e) { System.err.println("XML is not well-formed: " + e.getMessage()); return false; } }}2. 验证有效性使用 DTD 验证:public class XMLValidator { public static boolean isValidWithDTD(String xml, String dtd) { try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setValidating(true); DocumentBuilder builder = factory.newDocumentBuilder(); builder.setErrorHandler(new ErrorHandler() { public void warning(SAXParseException e) { System.err.println("Warning: " + e.getMessage()); } public void error(SAXParseException e) { System.err.println("Error: " + e.getMessage()); } public void fatalError(SAXParseException e) { System.err.println("Fatal Error: " + e.getMessage()); } }); Document document = builder.parse(new InputSource(new StringReader(xml))); return true; } catch (Exception e) { System.err.println("XML is not valid: " + e.getMessage()); return false; } }}使用 Schema 验证:public class XMLValidator { public static boolean isValidWithSchema(String xml, String schemaPath) { try { SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); Schema schema = factory.newSchema(new File(schemaPath)); DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); docFactory.setSchema(schema); DocumentBuilder builder = docFactory.newDocumentBuilder(); builder.setErrorHandler(new ErrorHandler() { public void warning(SAXParseException e) { System.err.println("Warning: " + e.getMessage()); } public void error(SAXParseException e) { System.err.println("Error: " + e.getMessage()); } public void fatalError(SAXParseException e) { System.err.println("Fatal Error: " + e.getMessage()); } }); Document document = builder.parse(new InputSource(new StringReader(xml))); return true; } catch (Exception e) { System.err.println("XML is not valid: " + e.getMessage()); return false; } }}最佳实践始终确保格式良好:格式良好是 XML 文档的基本要求使用 Schema 验证:Schema 比 DTD 更强大、更灵活开发时启用验证:在开发阶段启用验证,生产环境可根据需求调整处理验证错误:提供清晰的错误信息,帮助调试使用标准工具:使用成熟的 XML 解析器和验证工具文档化约束:清楚地文档化数据结构和约束版本控制:对 Schema 和 DTD 进行版本控制格式良好和有效性是 XML 文档质量的两个重要维度。格式良好确保文档可以被解析,而有效性确保文档符合业务规则和数据结构要求。在实际应用中,应该根据需求选择适当的验证级别。
计算机基础阅读 02月21日 14:22

什么是 XML 注入攻击,如何防止 XXE 攻击?

XML 注入是一种安全漏洞,攻击者通过在 XML 输入中注入恶意内容来操纵应用程序的 XML 解析行为。这种攻击可能导致信息泄露、拒绝服务、权限提升等安全问题。XML 注入的类型1. XML 外部实体(XXE)攻击XXE(XML External Entity)是最常见的 XML 注入攻击类型。攻击者利用 XML 解析器处理外部实体的能力来读取服务器上的敏感文件或发起 SSRF 攻击。攻击示例:<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE data [ <!ENTITY xxe SYSTEM "file:///etc/passwd">]><data> <username>&xxe;</username></data>危害:读取服务器上的敏感文件发起 SSRF(服务器端请求伪造)攻击执行远程代码执行(在某些情况下)拒绝服务攻击2. XML 注入攻击攻击者通过注入 XML 标签来修改 XML 文档的结构。攻击示例:<!-- 正常输入 --><user> <name>John</name></user><!-- 恶意输入 --><user> <name>John</name> <role>admin</role></user>3. XPath 注入类似于 SQL 注入,攻击者通过操纵 XPath 查询来获取未授权的数据。攻击示例:<!-- 正常查询 -->//user[username='john' and password='secret']<!-- 恶意查询 -->//user[username='john' or '1'='1']防护措施1. 禁用外部实体Java 示例:DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();// 禁用外部实体dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);dbf.setXIncludeAware(false);dbf.setExpandEntityReferences(false);DocumentBuilder db = dbf.newDocumentBuilder();Python 示例:from lxml import etree# 禁用外部实体parser = etree.XMLParser(resolve_entities=False, load_dtd=False)tree = etree.parse("data.xml", parser)PHP 示例:libxml_disable_entity_loader(true);$xml = simplexml_load_string($xmlString);2. 输入验证和过滤// 验证 XML 输入public boolean isValidXML(String xml) { try { // 检查是否包含恶意内容 if (xml.contains("<!DOCTYPE") || xml.contains("<!ENTITY")) { return false; } // 验证 XML 格式 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); DocumentBuilder db = dbf.newDocumentBuilder(); db.parse(new InputSource(new StringReader(xml))); return true; } catch (Exception e) { return false; }}3. 使用白名单验证// 白名单验证public boolean isValidElement(String elementName) { Set<String> allowedElements = new HashSet<>(Arrays.asList( "name", "email", "age" )); return allowedElements.contains(elementName);}4. 使用安全的 XML 解析器推荐的安全配置:DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();// 安全配置dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);dbf.setXIncludeAware(false);dbf.setExpandEntityReferences(false);// 使用 Schema 验证dbf.setNamespaceAware(true);dbf.setSchema(schema);5. XPath 注入防护// 使用参数化查询public User getUser(String username, String password) { try { XPathFactory xPathFactory = XPathFactory.newInstance(); XPath xpath = xPathFactory.newXPath(); // 使用变量而不是字符串拼接 XPathExpression expr = xpath.compile( "//user[username=$username and password=$password]" ); XPathExpression expr = xpath.compile( "//user[username=$username and password=$password]" ); // 设置参数 SimpleVariableResolver resolver = new SimpleVariableResolver(); resolver.addVariable(new QName("username"), username); resolver.addVariable(new QName("password"), password); xpath.setXPathVariableResolver(resolver); Node node = (Node) expr.evaluate(doc, XPathConstants.NODE); return parseUser(node); } catch (Exception e) { throw new RuntimeException("XPath query failed", e); }}6. 使用 XML Schema 验证// 使用 Schema 验证输入SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);Schema schema = factory.newSchema(new File("schema.xsd"));DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();dbf.setSchema(schema);dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);DocumentBuilder db = dbf.newDocumentBuilder();Document doc = db.parse(new File("input.xml"));最佳实践禁用 DTD 和外部实体:这是防止 XXE 攻击的最有效方法使用白名单验证:只允许预定义的元素和属性输入过滤:过滤掉危险的 XML 结构使用安全的解析器:配置解析器以禁用危险功能最小权限原则:限制 XML 处理的权限定期更新:保持 XML 解析库的最新版本安全编码:遵循安全编码最佳实践安全测试:定期进行安全测试和代码审查检测工具OWASP ZAP:Web 应用安全扫描器Burp Suite:Web 应用安全测试工具XMLSec:XML 安全库SonarQube:代码质量和安全分析工具XML 注入是一个严重的安全问题,必须在设计和实现阶段就考虑防护措施。通过禁用外部实体、输入验证和使用安全的解析器配置,可以有效地防止 XML 注入攻击。
计算机基础阅读 02月21日 14:22

什么是 XML 解析,DOM 和 SAX 解析有什么区别?

XML 解析是将 XML 文档转换为应用程序可以处理的数据结构的过程。主要有两种解析方式:DOM(文档对象模型)和 SAX(简单 API for XML)。DOM 解析DOM 是一种基于树形结构的解析方式,它将整个 XML 文档加载到内存中,构建一个树形结构。DOM 解析的特点内存占用大:需要将整个文档加载到内存随机访问:可以随机访问文档的任何部分双向遍历:可以向前和向后遍历文档修改能力:可以修改文档的结构和内容适合小文档:适合处理较小的 XML 文档DOM 解析示例(Java)DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();DocumentBuilder builder = factory.newDocumentBuilder();Document document = builder.parse(new File("data.xml"));// 获取根元素Element root = document.getDocumentElement();// 获取所有 book 元素NodeList books = root.getElementsByTagName("book");for (int i = 0; i < books.getLength(); i++) { Element book = (Element) books.item(i); String title = book.getElementsByTagName("title") .item(0) .getTextContent(); System.out.println("Title: " + title);}SAX 解析SAX 是一种基于事件的解析方式,它逐行读取 XML 文档,在遇到特定元素时触发事件。SAX 解析的特点内存占用小:不需要将整个文档加载到内存顺序访问:只能顺序访问文档单向遍历:只能向前遍历只读模式:不能修改文档适合大文档:适合处理大型 XML 文档SAX 解析示例(Java)SAXParserFactory factory = SAXParserFactory.newInstance();SAXParser saxParser = factory.newSAXParser();DefaultHandler handler = new DefaultHandler() { boolean inTitle = false; public void startElement(String uri, String localName, String qName, Attributes attributes) { if (qName.equals("title")) { inTitle = true; } } public void characters(char[] ch, int start, int length) { if (inTitle) { System.out.println("Title: " + new String(ch, start, length)); } } public void endElement(String uri, String localName, String qName) { if (qName.equals("title")) { inTitle = false; } }};saxParser.parse(new File("data.xml"), handler);DOM 与 SAX 的对比| 特性 | DOM | SAX ||------|-----|-----|| 内存占用 | 高 | 低 || 访问方式 | 随机访问 | 顺序访问 || 遍历方向 | 双向 | 单向 || 修改能力 | 可修改 | 只读 || 解析速度 | 较慢 | 较快 || 适用场景 | 小文档、需要修改 | 大文档、只读 |其他解析方式1. StAX(Streaming API for XML)StAX 是一种拉式解析方式,结合了 DOM 和 SAX 的优点。XMLInputFactory factory = XMLInputFactory.newInstance();XMLStreamReader reader = factory.createXMLStreamReader(new FileInputStream("data.xml"));while (reader.hasNext()) { int event = reader.next(); if (event == XMLStreamConstants.START_ELEMENT && reader.getLocalName().equals("title")) { System.out.println("Title: " + reader.getElementText()); }}2. JAXB(Java Architecture for XML Binding)JAXB 提供了 XML 与 Java 对象之间的自动绑定。JAXBContext context = JAXBContext.newInstance(Book.class);Unmarshaller unmarshaller = context.createUnmarshaller();Book book = (Book) unmarshaller.unmarshal(new File("book.xml"));选择解析方式的建议选择 DOM:当需要随机访问、修改文档,且文档较小时选择 SAX:当处理大型文档,且只需要顺序读取时选择 StAX:当需要更好的性能和更灵活的控制时选择 JAXB:当需要在 XML 和对象模型之间进行转换时性能优化建议使用合适的解析器:根据文档大小和需求选择合适的解析方式启用验证:在开发时启用 Schema 验证,生产环境可关闭以提高性能缓存解析结果:对于频繁访问的文档,缓存解析结果使用流式处理:对于大型文档,使用 SAX 或 StAX 进行流式处理XML 解析是处理 XML 数据的核心技术,选择合适的解析方式可以显著提高应用程序的性能和可维护性。
计算机基础阅读 02月21日 14:22

什么是 XPath,如何在 XML 中使用它来查询数据?

XPath(XML Path Language)是一种用于在 XML 文档中定位和选择节点的语言。它提供了一种简洁而强大的方式来查询 XML 文档中的数据,类似于 SQL 在关系数据库中的作用。XPath 的基本概念节点类型XPath 将 XML 文档视为节点树,包含以下节点类型:元素节点:XML 元素属性节点:元素的属性文本节点:元素或属性中的文本内容命名空间节点:元素的命名空间处理指令节点:XML 处理指令注释节点:XML 注释文档节点:整个文档的根节点XPath 语法1. 基本路径表达式<!-- 示例 XML --><bookstore> <book category="web"> <title lang="en">XML Guide</title> <author>John Doe</author> <price>39.95</price> </book> <book category="database"> <title lang="en">SQL Basics</title> <author>Jane Smith</author> <price>29.99</price> </book></bookstore>/* 选择文档节点(根节点)/bookstore 选择根元素 bookstore/bookstore/book 选择 bookstore 下的所有 book 元素//book 选择文档中所有的 book 元素bookstore//book 选择 bookstore 后代中的所有 book 元素2. 谓语(Predicates)谓语用于查找特定的节点,放在方括号 [] 中:/bookstore/book[1] 选择第一个 book 元素/bookstore/book[last()] 选择最后一个 book 元素/bookstore/book[position()<3] 选择前两个 book 元素//book[@category='web'] 选择 category 属性为 'web' 的 book 元素//book[price>35] 选择 price 大于 35 的 book 元素3. 通配符* 匹配任何元素节点@* 匹配任何属性节点node() 匹配任何类型的节点//book/* 选择 book 元素的所有子元素4. 轴(Axes)轴定义了相对于当前节点的节点集合:ancestor 选择当前节点的所有祖先节点ancestor-or-self 选择当前节点及其所有祖先节点attribute 选择当前节点的所有属性节点child 选择当前节点的所有子节点descendant 选择当前节点的所有后代节点descendant-or-self 选择当前节点及其所有后代节点following 选择文档中当前节点之后的所有节点following-sibling 选择当前节点之后的所有同级节点namespace 选择当前节点的所有命名空间节点parent 选择当前节点的父节点preceding 选择文档中当前节点之前的所有节点preceding-sibling 选择当前节点之前的所有同级节点self 选择当前节点本身5. 运算符算术运算符:+ - * div mod比较运算符:= != < > <= >=布尔运算符:and or not()XPath 函数节点集函数count(//book) 统计 book 元素的数量id('b1') 选择 ID 为 'b1' 的元素local-name() 返回节点的本地名称namespace-uri() 返回节点的命名空间 URIname() 返回节点的名称字符串函数string() 将节点转换为字符串concat('Hello', ' ', 'World') 连接字符串starts-with(text, 'XML') 检查是否以指定字符串开头contains(text, 'XML') 检查是否包含指定字符串substring(text, 1, 3) 提取子字符串string-length(text) 返回字符串长度normalize-space(text) 规范化空白字符translate(text, 'abc', 'XYZ') 字符替换布尔函数boolean() 转换为布尔值not() 逻辑非true() 返回 truefalse() 返回 falselang() 检查语言设置数字函数number() 转换为数字sum(//price) 计算总和floor(3.7) 向下取整ceiling(3.2) 向上取整round(3.7) 四舍五入XPath 实际应用示例1. Java 中使用 XPathimport javax.xml.xpath.*;import org.w3c.dom.*;XPathFactory factory = XPathFactory.newInstance();XPath xpath = factory.newXPath();// 解析 XML 文档DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();DocumentBuilder builder = docFactory.newDocumentBuilder();Document doc = builder.parse(new File("books.xml"));// 执行 XPath 查询String expression = "//book[@category='web']/title/text()";String title = xpath.evaluate(expression, doc, XPathConstants.STRING);System.out.println("Title: " + title);// 获取节点列表NodeList books = (NodeList) xpath.evaluate("//book", doc, XPathConstants.NODESET);for (int i = 0; i < books.getLength(); i++) { Element book = (Element) books.item(i); System.out.println(book.getAttribute("category"));}2. Python 中使用 XPath(lxml)from lxml import etree# 解析 XML 文档tree = etree.parse("books.xml")# 执行 XPath 查询titles = tree.xpath("//book[@category='web']/title/text()")for title in titles: print(f"Title: {title}")# 获取属性categories = tree.xpath("//book/@category")for category in categories: print(f"Category: {category}")# 使用函数total_price = sum(tree.xpath("//book/price/text()"))print(f"Total price: {total_price}")3. JavaScript 中使用 XPath// 解析 XMLconst parser = new DOMParser();const xmlDoc = parser.parseFromString(xmlString, "text/xml");// 执行 XPath 查询const result = xmlDoc.evaluate( "//book[@category='web']/title", xmlDoc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);for (let i = 0; i < result.snapshotLength; i++) { const node = result.snapshotItem(i); console.log(node.textContent);}XPath 与 XQuery 的关系XQuery 是基于 XPath 构建的查询语言,扩展了 XPath 的功能:XPath:用于定位和选择节点XQuery:用于查询、转换和构造 XML 数据XPath 最佳实践使用绝对路径:当文档结构固定时,使用绝对路径提高性能避免使用 //:// 会搜索整个文档,影响性能使用谓词过滤:尽早过滤节点,减少处理的数据量利用索引:在大型文档中,考虑使用索引优化查询缓存查询结果:对于频繁执行的查询,缓存结果XPath 是处理 XML 数据的强大工具,掌握 XPath 可以大大提高 XML 数据处理的效率和灵活性。