XML
1 什么是XML
XML(可扩展标记语言)是一种用于描述数据的标记语言,其设计目标是传输数据、数据的存储和数据的交换。XML 的设计宗旨是具有通用性、简单性和易用性,它为所有的语言和所有的操作系统提供了一种统一的数据格式。
2 XML格式约束
1.XML文档有且只有一个根元素
2.XML中的元素必须要有关闭标签(首尾呼应)
3.XML标签严格区分大小写
4.XML的属性需要加双引号
5.XML标签必须正确的嵌套
6.XMl中的特殊字符必须处理
7.XML中合法的使用标签名
特殊字符转义
> | > |
---|---|
< | < |
& | & |
’ | ' |
" | " |
3 XML中如何使用命名空间
XML命名空间的作用
解决在复杂、大型XML文件中,出现名称相同,但是含义不同的元素
定义命名空间
要在文档里使用XML命名空间,元素名就变成了限定名(qualified names 缩写为qName),限定名分成了两部分,一部分就是我们之前使用的元素名;另一部分是命名空间的前缀,它确定了这个名称所在的命名空间。
<a xmlns:b="http://www.atguigu.com/xml/b"> </a>/**a是根节点 **/
我们在根标签中添加了一个xmlns:b属性,xmlns代表的是xml namespace,b是我们声明的命名空间,但是b本身并没有意义,可以将它理解为是http://www.atguigu.com/xml/b的一个别名,我们在标签中使用b,就相当于使用这个uri地址。一旦使用了b这个前缀,就代表这个标签是属于http://www.atguigu.com/xml/b这个命名空间下的元素。
定义多个命名空间
我们还可以在一个文档中定义多个命名空间,如下的语法也是没有问题的:
<b:book xmlns:b="http://www.atguigu.com/xml/b" xmlns:a="http://www.atguigu.com/xml/a">
默认命名空间
<book xmlns="http://www.atguigu.com/xml/b" xmlns:a="http://www.atguigu.com/xml/a">
上边的xmlns="http://www.atguigu.com/xml/b"并没有指定前缀,那么这种没有指定前缀的命名空间就会作为页面中元素的默认命名空间,除非在标签中使用其他命名空间的前缀,否则解析器都会认为元素是在默认命名空间下存在。
但是要注意的是一个文档中只能有一个默认的命名空间,如下的语法是错误的:
<book xmlns="http://www.atguigu.com/xml/b"
xmlns="http://www.atguigu.com/xml/a">
4 XML的两种文档约束
在 XML 中,常见的两种文档约束是 DTD 和 XML Schema。
4.1DTD文档约束
DTD(Document Type Definition) 是一种基于 XML 的约束语言,它定义了 XML 文档中可以出现哪些元素和属性,以及它们之间的关系和规则。使用 DTD 可以确保 XML 文档的有效性和一致性。
DTD 一般以独立的 .dtd 文件形式存储,也可以直接嵌入到 XML 文档中进行定义。DTD 的定义包括元素(Element)、属性(Attribute)、实体(Entity)、注释(Comment)等。
元素(标签)定义
1.DTD声明
<!DOCTYPE [ ]>
声明用于定义 XML 文档的文档类型、元素、属性、数据类型等的规则。
2.元素(标签)的分类
<!ELEMENT 元素名称 EMPTY>-------------空元素 (一个标签例如: `<br/>`)
<!ELEMENT 元素名称 (#PCDATA)>--------文本元素(只有内容没有子标签)
<!ELEMENT 元素名称 (子元素名称1,子元素名称2,...)>----混合元素 (有子标签)
3.元素(标签)的限制
如果混合标签内有多个子标签用“ ,”隔开。
存在是非标签用“ | ”隔开(两个标签的情况下)。
允许标签出现零次或者一次用 “ ?”。
允许标签出现零次或者N次用 “ * ”。
允许标签出现一次或者N次用 “ + ”。
属性定义
1.DTD属性声明
<!ATTLIST 元素名称 属性名称 属性类型 设置说明>
2.type属性类型
CDATA:表示属性值是字符数据可以是任意字符。
ID:表示属性值是唯一标识符(ID),用于唯一标识文档中的某个元素。
IDREF:表示属性值是另一个元素的唯一标识符(ID)引用。
IDREFS:表示属性值是多个 IDREF 的空格分隔列表。
NMTOKEN:表示属性值是名称令牌(NMTOKEN),只能包含字母、数字、下划线、连字符等标识符字符。
NMTOKENS:表示属性值是多个 NMTOKEN 的空格分隔列表。
ENTITY:表示属性值是实体引用名称(ENTITY)。
ENTITIES:表示属性值是多个实体引用名称(ENTITY)的空格分隔列表。
NOTATION:表示属性值是标注名称(NOTATION)。
(XXX|XXX):表示选择标签 ,在后面可以设置默认值,但默认值只能是括号内的。
3.设置说明
#REQUIRED:属性值必须在元素上出现。
#IMPLIED:属性值是可选的。
#FIXED value:属性值必须是固定值 value。
注意:只有(XXX|XXX)选择标签的时候,desc才可以使用默认的方式并且写了默认值
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE persons [
<!--元素约束-->
<!ELEMENT persons (person*)>
<!ELEMENT person (name,age,contact,br?)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT age (#PCDATA)>
<!ELEMENT contact (phone|email)>
<!ELEMENT phone (#PCDATA)>
<!ELEMENT email (#PCDATA)>
<!ELEMENT br EMPTY>
<!--标签约束-->
<!ATTLIST
person pid ID #REQUIRED
sex (男|女|变态) "变态"
qq CDATA #IMPLIED
parent IDREF #IMPLIED
>
]>
<persons>
<person pid="p1" sex="男" qq="aaa" parent="p2">
<name>xxw</name>
<age>10</age>
<contact>
<phone>1234567</phone>
</contact>
<br />
</person>
<person pid="p2">
<name>xw</name>
<age>35</age>
<contact>
<email>123@qq.com</email>
</contact>
</person>
</persons>
4.2XML Schema 文档约束
XML Schema 是一种基于 XML 的语言,它是一种更强大和灵活的文档约束,可以定义比 DTD 更复杂且精确的数据模型。和 DTD 不同,XML Schema 可以通过命名空间(Namespace)来对不同的元素和属性进行区分。
XML Schema 的定义一般以 .xsd 文件形式存储,也可以作为声明性元素嵌入到 XML 文档中。XML Schema 的定义包括类型(Type)、元素(Element)、属性(Attribute)、注释(Annotation)等。
总的来说,XML Schema 比 DTD 更强大和灵活,但也更加复杂和繁琐,需要更多的学习和使用成本。
5 DOM解析XML
在解析XML前先了解一下以下这几个类(建议按表格顺序读,这也是解析xml的顺序):
类 | 作用 | 举例 |
---|---|---|
DocumentBuilderFactory | 创建(DocumentBuilder(文件构建者)对象)的工厂类 | 创建工厂对象:DocumentBuilderFactorydbFactory= DocumentBuilderFactory.newInstance(); |
DocumentBuilder | 文件构建者(创建文件Document对象) | 用工厂对象创建DocumentBuilder对象:DocumentBuilder db=dbFactory.newDocumentBuilder(); |
Document | 文件对象(将Xml文件加载放置在此对象中) | 将Xml文件加载放置Document对象中:Document document=db.parse(“url.xml”); |
NodeList | 节点集合 |
Document接口常用方法(Document可以看作是整个文档树,可以对文档树进行CRUD操作):
返回类型 | 方法 | 作用 |
---|---|---|
Element | createElement(String tagName) | 创建指定名称的元素节点 |
Text | createTextNode(String tagName) | 创建指定名称的Text文本节点 |
NodeList | getElementByTagName(String name) | 以文件顺序返回节点下所有名称为指定字符串的子孙节点 |
Element | getDocumentElement() | 返回文档的根节点 |
NodeList接口方法:
返回类型 | 方法 | 作用 |
---|---|---|
int | getLength() | 返回NodeList中的节点数 |
Node | item(int index) | 返回第index个节点 |
Node接口常用方法:
返回类型 | 方法 | 作用 |
---|---|---|
Node | appendChilid(Node newChild) throws DOMException | 在当前节点下增加一个子节点 |
NodeList | getChildNodes() | 取得本节点下的全部子孙节点 |
Node | getFirstChild() | 取得该节点下的第一个子节点 |
Node | getLastChild() | 取得本节点下的最后一个子节点 |
String | getNodeValue() throws DOMException | 获取节点内容 |
Node | getNextSibling() | 与该节点相邻的下一节点 |
Node | removeChild(Node child) | 删除子节点child |
Element接口常用方法:
返回类型 | 方法 | 作用 |
---|---|---|
String | getAttribute(String name) | 返回属性名为name的值 |
void | setAttribute(String name,String value) | 添加或设置name属性的值为value |
void | removeAttribute(String name) | 移除名称为name标的标签 |
String | getTagName() | 返回标签名 |
xml文件解析大致过程:
1.创建DocumentBuilderFactory工厂对象
2.用工厂对象创建DocumentBuilder对象
3.用DocumentBuilder对象将xml文件加载进内存,用Document对象接收
4.获取到所有person节点,返回NodeList
5.循环遍历NodeList里的person节点
6.用getElementByTagName();分别获取person标签节点里的name标签节点、email标签节点
7.用getFirstNode().getNodeValue();分别获取name标签节点、email标签节点的第一个子节点的内容
public static void main(String[] args) throws ParserConfigurationException, IOException, SAXException, TransformerException {
//调用newInstance静态方法创建DocumentBuilderFactory对象
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
//调用newDocumentBuilder方法创建DocumentBuilder对象
DocumentBuilder db = dbf.newDocumentBuilder();
Document document = db.parse("D:\\Project\\aCode\\test.Test\\AdvancedPogramming\\ch10\\xml\\test4.xml");
//获取根节点
Element root = document.getDocumentElement();
//新建一个品牌
Element brand = document.createElement("Brand");
//设置品牌名
brand.setAttribute("name","三星");
//添加到根节点下
root.appendChild(brand);
//在添加的品牌下添加一个型号
Element type1 = document.createElement("Type");
//设置型号名
type1.setAttribute("name","note4");
//添加到品牌下
brand.appendChild(type1);
//获取所有品牌
NodeList nodeList = document.getElementsByTagName("Brand");
for (int i = 0; i < nodeList.getLength(); i++) {
Element Brand = (Element) nodeList.item(i);
//如果品牌名等于华为,则删除
if (Brand.getAttribute("name").equals("华为")){
Brand.getParentNode().removeChild(Brand);
break;
}
}
for (int i = 0; i < nodeList.getLength(); i++) {
Element Brand = (Element) nodeList.item(i);
//给所有品牌设置id
Brand.setAttribute("id", "brand"+(i+1));
System.out.println("品牌名:" + Brand.getAttribute("name"));
//获取该品牌下所有型号
NodeList Type = Brand.getElementsByTagName("Type");
System.out.print("型号:");
for (int j = 0; j < Type.getLength(); j++) {
//遍历型号
Element type = (Element) Type.item(j);
System.out.print(type.getAttribute("name") + " ");
}
System.out.println();
}
//建立转换器工厂
TransformerFactory tff = TransformerFactory.newInstance();
//建立转换器
Transformer tf = tff.newTransformer();
//设置输出格式
tf.setOutputProperty(OutputKeys.INDENT,"yes");
//创建源
DOMSource source = new DOMSource(document);
//创建结果
StreamResult result = new StreamResult(new File("D:\\Project\\aCode\\test.Test\\AdvancedPogramming\\ch10\\xml\\new.xml"));
//转换
tf.transform(source,result);
}
6 修改并保存XML文件
步骤
1.获得TransformerFactory对象
2.创建Transformer对象
3.创建DOMSource对象
- 包含XML信息
4.设置输出属性
- 编码格式
5.创建StreamResult对象
- 包含保存文件的信息
6.将XML保存到指定文件中
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import java.io.File;
public class XmlDomExample {
public static void main(String[] args) {
try {
// 创建 DocumentBuilderFactory 实例
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
// 加载 XML 文档
Document doc = builder.parse(new File("input.xml"));
// 获取所有 <Brand> 标签
NodeList brandNodes = doc.getElementsByTagName("Brand");
// 遍历节点列表
for (int i = 0; i < brandNodes.getLength(); i++) {
Node node = brandNodes.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
Element brandElement = (Element) node;
// 为每个 <Brand> 标签添加 id 属性
String idValue = "brand" + (i + 1);
brandElement.setAttribute("id", idValue);
// 删除值为“华为”的 <Brand> 标签
if ("华为".equals(brandElement.getTextContent().trim())) {
// 获取父节点
Node parent = brandElement.getParentNode();
// 删除节点
parent.removeChild(brandElement);
// 由于节点已被删除,需将索引回退,确保所有节点被检查
i--;
}
}
}
// 保存修改后的 XML 文件
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
DOMSource source = new DOMSource(doc);
StreamResult result = new StreamResult(new File("output.xml"));
transformer.transform(source, result);
System.out.println("XML file modified and saved successfully.");
} catch (Exception e) {
e.printStackTrace();
}
}
}
7 使用 DOM4J 操作 XML 文件
7.1添加依赖
首先,你需要在你的项目中添加 DOM4J
依赖。如果你使用 Maven,可以在 pom.xml
文件中添加:
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.4</version> <!-- 请根据需要选择合适的版本 -->
</dependency>
7.2示例 XML 文件
假设我们有一个名为 phone_collection.xml
的 XML 文件,其内容如下:
<phones>
<phone>
<name>iPhone 15</name>
<brand>Apple</brand>
<price>6599</price>
</phone>
<phone>
<name>小米14</name>
<brand>小米</brand>
<price>3999</price>
</phone>
</phones>
7.3使用 DOM4J 操作 XML 文件
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;
public class PhoneManager {
private Document document;
private Element root;
public PhoneManager(String xmlFilePath) throws DocumentException {
SAXReader reader = new SAXReader();
document = reader.read(xmlFilePath);
root = document.getRootElement();
}
public void displayPhoneList() {
List<Element> phones = root.elements("phone");
for (Element phone : phones) {
String name = phone.elementText("name");
String brand = phone.elementText("brand");
String price = phone.elementText("price");
System.out.println("Name: " + name + ", Brand: " + brand + ", Price: " + price);
}
}
public void savePhoneList(String filename) throws IOException {
try (FileWriter fileWriter = new FileWriter(filename)) {
//输出格式控制
OutputFormat format = OutputFormat.createPrettyPrint();
//创造一个到指定目录的XML写入器
XMLWriter writer = new XMLWriter(fileWriter, format);
//将document对象内容通过写入器写入指定目录
writer.write(document);
}
}
public void addPhone(String name, String brand, String price) {
Element newPhone = root.addElement("phone");
newPhone.addElement("name").addText(name);
newPhone.addElement("brand").addText(brand);
newPhone.addElement("price").addText(price);
}
public void updatePhone(String name, String newPrice) {
List<Element> phones = root.elements("phone");
for (Element phone : phones) {
String phoneName = phone.elementText("name");
if (name.equals(phoneName)) {
phone.element("price").setText(newPrice);
}
}
}
public void deletePhone(String name) {
List<Element> phones = root.elements("phone");
for (Element phone : phones) {
String phoneName = phone.elementText("name");
if (name.equals(phoneName)) {
root.remove(phone);
break;
}
}
}
public static void main(String[] args) {
try {
PhoneManager manager = new PhoneManager("D:\\Project\\aCode\\test.Test\\AdvancedPogramming\\ch10\\xml\\phons.xml");
// 展示手机列表
manager.displayPhoneList();
// 添加一个新手机
manager.addPhone("vivox100", "vivo", "3999");
// 更新手机的价格
manager.updatePhone("小米14", "3599");
// 根据name删除一个手机
manager.deletePhone("iPhone 15");
//再次展示手机列表
System.out.println("-------------------------------------------");
manager.displayPhoneList();
// 保存文件到制定目录
manager.savePhoneList("D:\\Project\\aCode\\test.Test\\AdvancedPogramming\\ch10\\xml\\updtphons.xml");
} catch (DocumentException | IOException e) {
e.printStackTrace();
}
}
}