XML文件源码如下:
<?xml version="1.0" encoding="UTF-8"?>
<PhoneInfo>
<Brand name="华为">
<Type name="U8650"/>
</Brand>
<Brand name="苹果">
<Type name="iPhone4"/>
<Type name="iPhone5"/>
</Brand>
</PhoneInfo>
一、增
要在PhoneInfo的节点上添加品牌节点,需要先找到PhoneInfo节点。然后在此DOM树上创建一个新的Brand品牌节点,设置它的属性name为“MOTO”。然后根据它在DOM树的位置,把它添加为PhoneInfo的子节点。这样,此DOM树就有了新的结构。最后把这个DOM树结构保存到XML文件就可以了。
综上,实现步骤如下:
1、为XML文档构造DOM树
2、创建新节点,并设置name属性
3、把节点加到其所属父节点上
4、保存XML文档
其实整体的思路就是我先在DOM树外创建元素节点,然后再把元素节点添加到DOM树中,用到的方法主要是:
createElement(元素类型) 创建一个某种类型的元素
setAttribute(属性, 属性值) 为该元素添加属性
父元素.appendChild(子元素)添加父子关系
代码如下:
// 添加节点信息
public void addBrand() {
// 创建Brand节点
Element brandElement = document.createElement("Brand");
brandElement.setAttribute("name", "MOTO");
// 创建Type节点
Element typeElement = document.createElement("Type");
typeElement.setAttribute("name", "A1680");
// 添加父子关系
brandElement.appendChild(typeElement);
// 在DOM树外把标签为PhoneInfo的元素拿出来,以方便下一步把新建的节点加进去
Element phoneElement = (Element)document.getElementsByTagName("PhoneInfo").item(0);
// 添加父子关系
phoneElement.appendChild(brandElement);
// 把新的DOM树保存到XML文件之中
saveXML();
}
下面这行代码:
Element phoneElement = (Element)document.getElementsByTagName("PhoneInfo").item(0);
的意思是:通过Document拿到标签名字为PhoneInfo的标签,把该节点强转为Element类型后,然后再把新建的Brand节点加进去。
注:上面这些操作都是在操作内存中的DOM树,操作完毕之后内存中的DOM树变化了,但是XML文件并没有改变。还需要将内存中的DOM树保存到XML文件中。
二、保存
保存XML文件,需要借助转换器:源(最新的DOM树)-->目的地(手机信息.xml),借助输出流来实现。
代码如下:
// 保存XML文件
public void saveXML() {
// 转换器工厂
TransformerFactory transformerFactory = TransformerFactory.newInstance();
try {
// 转换器
Transformer transformer = transformerFactory.newTransformer();
DOMSource domSource = new DOMSource(document);
StreamResult result = new StreamResult(new FileOutputStream("手机信息.xml"));
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
// 转换器的方法,对应于解析器的解析方法(parse())
transformer.transform(domSource, result);
} catch (TransformerConfigurationException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (TransformerException e) {
e.printStackTrace();
}
}
分析:
1、保存XML文件需要借助转换器,这个跟解析XML文件时用的解析器相对应。如下:
转换器工厂对应解析器工厂TransformerFactory transformerFactory = TransformerFactory.newInstance();
对应DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
;转换器对应解析器Transformer transformer = transformerFactory.newTransformer();
对应DocumentBuilder db = factory.newDocumentBuilder();
;转换方法对应解析方法:parse()
对应transform()
。两个方法需要的参数不一样,返回的结果也不一样。
parse()方法需要的参数是一个XML文件,返回结果是一个DOM树。transform()方法需要的参数是(要保存的DOM源和一个Result)(transformer.transform(Source, Result);
)。
2、对于transform()方法中的两个参数,Source和Result,在XML文档的保存中分别是DOMSource和StreamResult。DOMSource很简单,就是直接把document作为参数传进去即可:DOMSource domSource = new DOMSource(document);
。
但是StreamResult构造对象时有三种常用构造方法,参数分别不同:
第一种:StreamResult result = new StreamResult("手机信息.xml");
直接把文件传进来作为参数,但是此时需要制定文档编码格式,否则可能在最后的XML文件中出现乱码。指定字符编码格式代码如下:transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
。
第二种:StreamResult result = new StreamResult(new FileOutputStream("手机信息.xml"));
将一个OutputStream类型的数据作为参数传进去。但是此时也需要制定编码格式:transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
。
第三种:将一个Writer类型的数据作为参数传进去:
OutputStream out = new FileOutputStream("手机信息.xml");
StreamResult result = new StreamResult(new OutputStreamWriter(out, "UTF-8"));
此时不需要前两种方法的指定编码格式了,因为OutputStreamWriter()对象在输出是可以直接指定编码格式。
保存之后的XML文件如下:
<?xml version="1.0" encoding="UTF-8" standalone="no"?><PhoneInfo>
<Brand name="华为">
<Type name="123"/>
<Type name="456"/>
<Type name="789"/>
</Brand>
<Brand name="iphone">
<Type name="iphone6"/>
<Type name="iphone7"/>
<Type name="iphone4"/>
</Brand>
<Brand name="香蕉"><Type name="A1680"/></Brand></PhoneInfo>
注1:OutputKeys.ENCODING
意思是设置输出时候的编码。OutputKeys
是输出时候的关键字,里面定义了一些静态方法,可以设置输出时候的一些格式等。
注2:如果出现中文乱码,需要
StreamResult result = new StreamResult(new OutputStreamWriter(out, "UTF-8"));
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
两句同时写上才有用。
三、设置缩进
从上面保存后的XML文件来看,只是增加在最后一行,没有设置缩进,看着非常别扭。那怎么设置缩进呢?
设置缩进分两步:
第一步:允许缩进
transformerFactory.setAttribute("indent-number", 4);
通过调用转换器工厂的这个方法可以设置缩进的空格数目。
第二步:允许缩进
transformer.setOutputProperty(OutputKeys.INDENT, "YES");
在转换器中把OutputKeys
的关键字INDENT
相应的值设置为YES
,这就表示允许在将内存中的DOM树写进XML文件的时候设置缩进。
加上设置缩进的代码之后,代码如下:
public void saveXML() {
// 转换器工厂
TransformerFactory transformerFactory = TransformerFactory.newInstance();
// 设置缩进
transformerFactory.setAttribute("indent-number", 4);
try {
// 转换器
Transformer transformer = transformerFactory.newTransformer();
DOMSource domSource = new DOMSource(document);
OutputStream out = new FileOutputStream("手机信息.xml");
transformer.setOutputProperty(OutputKeys.INDENT, "YES");
StreamResult result = new StreamResult(new OutputStreamWriter(out, "UTF-8"));
transformer.transform(domSource, result);
} catch (TransformerConfigurationException e) {
e.printStackTrace();
} catch (TransformerException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
最终的XML文件如下:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<PhoneInfo>
<Brand name="华为">
<Type name="123"/>
<Type name="456"/>
<Type name="789"/>
</Brand>
<Brand name="iphone">
<Type name="iphone6"/>
<Type name="iphone7"/>
<Type name="iphone4"/>
</Brand>
<Brand name="香蕉">
<Type name="A1680"/>
</Brand>
</PhoneInfo>
注:倒数第四行是没有缩进的,但这不是代码的问题,而是MyEclipse底层源码的问题,忽略不计即可。