下面是利用document对xml文件进行修改的大致过程:
java中使用XML,目前常用的就是Jaxp(sun)和dom4j了,这里先讲讲java自带的Jaxp包
JAXP 开发包是J2SE的一部分,它由javax.xml、org.w3c.dom、org.xml.sax包及其子包组成
Jaxp的xml解析器有两种,一种是DOM解析器,一种是SAX解析器,两种各自应用在不同的场景上。
DOM:
DOM分析器通过对XML文档的分析,把整个XML文档以一棵DOM树的形式存放在内存中,应用程序可以随时对DOM树中的任何一个部分进行访问与操作,也就是说,通过DOM树,应用程序可以对XML文档进行随机访问。这种访问方式给应用程序的开发带来了很大的灵活性,它可以任意地控制整个XML文档中的内容。然而,由于DOM分析器把整个XML文档转化成DOM树放在了内存中,因此,当XML文档比较大或者文档结构比较复杂时,对内存的需求就比较高。而且,对于结构复杂的树的遍历也是一项比较耗时的操作。所以,DOM分析器对机器性能的要求比较高,实现效率不十分理想。不过,由于DOM分析器的树结构的思想与XML文档的结构相吻合,而且,通过DOM树机制很容实现随机访问。因此DOM分析器也有较为广泛的使用价值。
在DOM解析时,会把xml中各个节点视为对象,然后根据父子关系相互嵌套。优点时容易操纵,缺点也很明显,必须全部通读xml并加载进内存。
DOM解析的流程:
1。DocumentBuilderFactory是抽象类,newInstance()方法会根据本地平台安装的xml解析器自动创建相应的工厂实例
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
2。DocumentBuilder的newDocumentBuilder()方法会得到本地xml解析器相应的DocumentBuilder实例
DocumentBuilder documentB = dbf.newDocumentBuilder();
3。解析xml,根据DocumentBuilder的parse方法。
Document document = documentB.parse(newFile(path));
//看文档可以知道,parse可以从解析File、InputSource(经过包装的InputStream)、InputStream和URI字符串
4,Document代表了整个XML文档,首先获取根节点,Element就是标签,它们都属于Node,也就是说,都实现了Node接口
Element root=document.getDocumentElement();
5,下面就是增删改查了
增加结点
//增加一个结点,注意,增加节点是对document操作,Document实现了这个方法
Element newStudent=document.createElement("student");
//增加一个属性,属性也算是结点,一切都是Node
Attr cid=document.createAttribute("idcard");
//给属性设置值
cid.setValue("1121");
//将属性添加进这个标签
newStudent.setAttributeNode(cid);
//最后,将这个结点添加进根节点,注意,是对根节点添加,不是Document
root.appendChild(newStudent);
提取某节点信息(查)
//首先根据第3步,已经获得根结点,也就是Element root
//获取所有标签名为sutdent的节点集合
NodeList students=root.getElementsByTagName("student");
//至于获取标签属性以及text内容,自行看手册
//要注意的是,换行和制表符'\n\t'这些,也会被当做text内容解析
//至于xml的编码问题,都是自动的,不用手动设置了。
更改结点内容(很多都是Node接口的方法,自行查看手册即可)
//设置标签内容
Node.setTextContent(String text)
//设置属性内容
Attr.setValue(String value)
删除结点
Node.removeChild(Node node)
6,操作完xml,保存结果
javax.xml.transform包中的Transformer类用于把代表XML文件的Document对象转换为某种格式后进行输出,例如把xml文件应用样式表后转成一个html文档。
利用这个对象,当然也可以把Document对象又重新写入到一个XML文件中。
//1、获得Transformer工厂
TransformerFactory tff=TransformerFactory.newInstance();
//2、对于DOM对象,使用树来表示,肯定是个多叉树了,,,
//这个类,就是将树,变为结果树
Transformer tf = tff.newTransformer();
//3、把document(DOM)转换为xml source
Source sc=newDOMSource(document);
//4、创建一个DOM目标,这里是个流
Result rs=newStreamResult(newFile(path_URI));
//5、将 XML Source转换为 Result即writer,这样就写入数据流了
tf.transform(sc, rs);
DOM解析就到这里,如果以网络流读取一个大的xml文件的话,这样肯定是不行的,不可能一直等到它全部读完载入内存再操作吧。。。。。光读取的话,就用SAX了。
SAX:
SAX分析器在对XML文档进行分析时,触发一系列的事件,应用程序通过事件处理函数实现对XML文档的访问。由于事件触发本身是有时序性的,因此,SAX分析器提供的是一种对XML文档的顺序访问机制,对于已经分析过的部分,不能再倒回去重新处理。
SAX之所以被叫做"简单"应用程序接口,是因为SAX分析器只做了一些简单的工作,大部分工作还要由应用程序自己去做。也就是说,SAX分析器在实现时,它只是顺序地检查XML文档中的字节流,判断当前字节是XML语法中的哪一部分,检查是否符合XML语法并触发相应的事件。对于事件处理函数本身,要由应用程序自己来实现。同DOM分析器相比,SAX分析器对XML文档的处理缺乏一定的灵活性,然而,对于那些只需要访问XML文档中的数据而不对文档进行更改的应用程序来说,SAX分析器的效率则更高。由于SAX分析器实现简单,对内存要求比较低,因此实现效率比较高同时具有广泛的应用价值。
下面是一个较为完整的例子:
用java向XML增加一个结点元素是今年ACCP认证考试的试题,可见XML的重要性!
题目要求考生向已经给出的XML文件增加一个结点元素,答案如下:
//DomXML.java
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.*;
class XmlWrite
{
public static void main(String[] args)
{
try{
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder=factory.newDocumentBuilder();
Document doc=builder.parse("links.xml");
doc.normalize();
String name2="Hanzhong´s Homepage";
String sex2="male";
String phone2="22324098";
//create an element named "link"
Element link=doc.createElement("link");
/*create an element of context named "name"
context is consided with an element by w3c */
Element linkName=doc.createElement("name");
Text textName=doc.createTextNode(name2);
linkName.appendChild(textName);
link.appendChild(linkName);
doc.getDocumentElement().appendChild(link);//return a root node of document
TransformerFactory tFactory =TransformerFactory.newInstance();
Transformer transformer = tFactory.newTransformer();
DOMSource source = new DOMSource(doc);
StreamResult result = new StreamResult(new java.io.File("links.xml"));
transformer.transform(source, result);
}
catch(Exception e){}
}
}
将代码封装起来:
获得Document,即xml文件的树模型
public static Document getDocument(String xmlPath) {
Document document = null;
// 建立DocumentBuilderFactory对象
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder;
try {
// 建立DocumentBuilder对象
builder = factory.newDocumentBuilder();
// 用DocumentBuilder对象的parse方法引入文件建立Document对象
document = builder.parse("file:\\" + xmlPath);
} catch (Exception e) {
// e.printStackTrace();
return null;
}
return document;
}
获得Document后,增加或删除节点后输出xml文件
public static void writeToXML(String path, Document document) {
try {
FileOutputStream fos = new FileOutputStream(path);
OutputStreamWriter outwriter = new OutputStreamWriter(fos);
callWriteXmlFile(document, outwriter, "gb2312");
outwriter.close();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
操作完xml,保存结果
private static void callWriteXmlFile(Document doc, Writer w, String encoding) {
try {
DOMSource source = new DOMSource(doc);
StreamResult result = new StreamResult(w);
Transformer xformer = TransformerFactory.newInstance()
.newTransformer();
xformer.setOutputProperty(OutputKeys.ENCODING, encoding);//设置xml的编码
xformer.setOutputProperty(OutputKeys.VERSION, "1.0");
xformer.transform(source, result);
xformer.setOutputProperty(OutputKeys.INDENT, "yes");//用于设置缩进的
} catch (TransformerConfigurationException e) {
e.printStackTrace();
} catch (TransformerException e) {
e.printStackTrace();
}
}
现在我们来看一段DES加密、解密的代码
import java.io.*;
import javax.crypto.*;
import java.security.*;
public class DESCryptoTest {
public static void main(String[] args) {
// Security.addProvider(new com.sun.crypto.provider.SunJCE());
// Security.addProvider(new
// org.bouncycastle.jce.provider.BouncyCastleProvider());
KeyGenerator kg = null;
try {
// 指定算法,这里为DES;如果想用Blowfish算法,则用 getInstance("Blowfish")
// BouncyCastle基本上支持所有通用标准算法
kg = KeyGenerator.getInstance("DES");
// 指定密钥长度,长度越高,加密强度越大
kg.init(56);
// 产生密钥
Key key = kg.generateKey();
System.out.println("Key format: " + key.getFormat());//RAW
System.out.println("Key algorithm: " + key.getAlgorithm());//DES
// 加密要用Cipher来实现
Cipher cipher = Cipher.getInstance("DES");
System.out.println("Cipher provider: " + cipher.getProvider());//SunJCE version 1.6
System.out.println("Cipher algorithm: " + cipher.getAlgorithm());//DES
//**************
byte[] data = "Hello World!".getBytes();
System.out.println("Original data : [" + data.length + "]"
+ new String(data));//[12]Hello World!
// 设置加密模式
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] result = cipher.doFinal(data);
System.out.println("Encrypted data: [" + result.length + "]"
+ new String(result));//[16]C澵K墙憇`禃??
// 设置解密模式
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] original = cipher.doFinal(result);
System.out.println("Decrypted data: [" + original.length + "]"
+ new String(original));//[12]Hello World!
//*******************
String filename = "d:\\待加密.xml";
// 读入并加密文件
try {
// 输入流
cipher.init(Cipher.ENCRYPT_MODE, key);
BufferedInputStream in = new BufferedInputStream(
new FileInputStream(filename));
// 输出流
CipherOutputStream out = new CipherOutputStream(
new BufferedOutputStream(new FileOutputStream(
"d:\\加密后.xml")), cipher);
int i;
do {
i = in.read();
if (i != -1)
out.write(i);
} while (i != -1);
in.close();
out.close();
System.out.println("加密文件完成!");
} catch (Exception ey5) {
System.out.println("Error when encrypt the file");
System.exit(0);
}
try {
cipher.init(Cipher.DECRYPT_MODE, key);
// 输出流
BufferedOutputStream out = new BufferedOutputStream(
new FileOutputStream("d:\\解密后.xml"));
// 输入流
CipherInputStream in = new CipherInputStream(
new BufferedInputStream(new FileInputStream(
"d:\\加密后.xml")), cipher);
int i;
do {
i = in.read();
if (i != -1)
out.write(i);
} while (i != -1);
in.close();
out.close();
System.out.println("解密文件完成!");
} catch (Exception ey5) {
System.out.println("Error when encrypt the file");
System.exit(0);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
要向writeToXML方法中加入加密的代码
起初的思路是:增删节点后---写出一个xml文件---读xml文件到内存---加密--输出加密后的文件----删除原未加密的文件
这种思路较好实现,但是有过多IO操作
后来的思路是:将xml文件的读写操作删掉,直接在内存中队xml加密
这就需要将Document转化为inputStream,再通过CipherOutputStream加密后输出
将Document转化为inputStream:
一个简单需求,要将Document对象转成InputStream,在网上也没有找到方法。
辗转的解决了下:
//创建Document对象:
Document xmldoc= new DocumentImpl();
Element root = xmldoc.createElement("USERS");
//转成String:
StringWriter strResponse = null;
OutputFormat format = new OutputFormat(xmldoc);
format.setEncoding("ISO-8859-1");
format.setStandalone(true);
format.setIndenting(true);
format.setIndent(2);
format.setLineSeparator(LineSeparator.Windows);
strResponse = new StringWriter();
XMLSerializer serializer = new XMLSerializer(strResponse, format);
try {
serializer.asDOMSerializer();
serializer.serialize(xmldoc);
} catch (IOException ex1) {
}
StringBuffer sb = strResponse.getBuffer();
//再由String转成InputStream
InputStream inputStream = new ByteArrayInputStream(sb.toString().getBytes());
所以writeToXML方法可以修改为
public static void writeToXML(String path, Document document) {
String filename = path;
System.out.println(path);
try {
// 创建Document对象:
// 转成String:
StringWriter strResponse = null;
OutputFormat format = new OutputFormat(document);
format.setEncoding("GB2312");
// format.setStandalone(false);
// format.setIndenting(true);
// format.setIndent(2);
// format.setLineSeparator(LineSeparator.Windows);
strResponse = new StringWriter();
XMLSerializer serializer = new XMLSerializer(strResponse, format);
try {
serializer.asDOMSerializer();
serializer.serialize(document);
} catch (IOException ex1) {
}
StringBuffer sb = strResponse.getBuffer();
// 再由String转成InputStream
InputStream inputStream = new ByteArrayInputStream(sb.toString()
.getBytes());
//
// } catch (Exception e) {
// e.printStackTrace();
// }
// try {
// 生成一个实现指定转换的 Cipher 对象
// 加密要用Cipher来实现
Cipher cipher = Cipher.getInstance("DES");
// 产生密钥
Key key = CreateMyRegularKey("DES");
System.out.println("Key format: " + key.getFormat());// RAW
System.out.println("Key algorithm: " + key.getAlgorithm());// DES
// 读入并加密文件
try {
// 输入流
// 用密钥和随机源初始化此 cipher
cipher.init(Cipher.ENCRYPT_MODE, key);
BufferedInputStream in = new BufferedInputStream(inputStream);
// 输出流
CipherOutputStream out = new CipherOutputStream(
new BufferedOutputStream(new FileOutputStream(path)),
cipher);// 加密后.xml
int i;
do {
i = in.read();
if (i != -1)
out.write(i);
} while (i != -1);
in.close();
out.close();
inputStream.close();
System.out.println("加密文件完成!");
} catch (Exception ey5) {
System.out.println("Error when encrypt the file");
System.exit(0);
}
} catch (Exception e) {
e.printStackTrace();
}
}
在getDocument方法中加入解密的代码
public static Document getDocument(String xmlPath) {
Document document = null;
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder;
try {
// 解密要用Cipher来实现
Cipher cipher = Cipher.getInstance("DES");
System.out.println("get....Cipher algorithm: "
+ cipher.getAlgorithm());// DES
Key key = CreateMyRegularKey("DES");
// 解密模式
cipher.init(Cipher.DECRYPT_MODE, key);
// 输入流
CipherInputStream in = new CipherInputStream(
new BufferedInputStream(new FileInputStream(xmlPath)),
cipher);
builder = factory.newDocumentBuilder();//parse可以解析file、InputSource、InputStream和URL字符串document = builder.parse(in);
System.out.println("in");
in.close();
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
return document;
}