标签:jdom dom4j xml解析 xpath语法 jaxp
XML解析的详细分析与jaxp解析XML详见:XML学习笔记(四):使用 DOM和SAX 解析XML
一、JDom
1、创建XML文件:
1)Document类即代表整个XML文档,把生成的 Document 利用 XMLOutputter 类输出即可。
2)映射关系:元素:Element;属性:Attribute;注解:Comment;文本信息:Text;
3)注意:addContent()是追加,setContent()会覆盖。
/**
* 创建XML
*
* @throws IOException
*/
private void createXML() {
ArrayList<Element> eleList = new ArrayList<Element>();
Document doc = null;
try {
// 新建一个Document
doc = new Document();
// 新建一个根元素 <root>
Element root = new Element("root");
// 给根元素添加文本内容
root.setText("this is a root el");
// 修改根元素 的名字
root.setName("rootElement");
// 给根元素添加属性
root.setAttribute("name", "属性");
// 新建一个元素<APPNT>
Element appntEle = new Element("APPNT");
// 定义一个属性对象
Attribute appntAttr = new Attribute("name", "SAM-SHO");
// 给<APPNT>元素添加属性
appntEle.setAttribute(appntAttr);
appntEle.setAttribute("age", "28");
// 给<APPNT>元素添加文本内容
appntEle.setText("我是投保人");
// 给<APPNT>元素添加注解
Comment comment = new Comment("我是注解");
appntEle.addContent(comment);
// 新建一个元素<INSURED>
Element insuredEle = new Element("INSURED");
// 定义一个属性对象
Attribute insuredAttr = new Attribute("name", "SAMMY");
// 给<INSURED>元素添加属性
insuredEle.setAttribute(insuredAttr);
insuredEle.setAttribute("age", "49");
// 给<INSURED>元素添加文本内容
Text text = new Text("我是被保人,可以这样加文本");
// insuredEle.setText("我是被保人");
insuredEle.addContent(text);
insuredEle.addContent(" ||我是追加");
// addContent()是追加,setContent()会覆盖
// insuredEle.setContent(new Text("....我会覆盖"));
// 把新建的元素放入集合
eleList.add(appntEle);
eleList.add(insuredEle);
// 把新建的元素添加到 根元素 <root>
root.setContent(eleList);
// 也可以使用addContent()一个一个加
// root.addContent(appntEle);
// root.addContent(insuredEle);
// 把根元素添加到 Document
doc.setRootElement(root);
// doc = new
// Document(root);//可以直接这样新建xml文档,同doc.setRootElement(root)
String xmlStr = out.outputString(doc);
System.out.println(xmlStr);// 打印到控制台
out.output(doc, System.out);// 输出到控制台
out.output(doc, new FileOutputStream("resources/root.xml"));// 输出到文件
} catch (IOException e) {
e.printStackTrace();
}
}
1)只要有一点Dom解析的基础,JDom的使用就会变得比较简单。
2)得到解析器、解析XML文件的步骤都是固定的。
3)我们获取到元素与属性后,可以直接输出,也可以set到预先定义好的JavaBean中。
/**
* 解析XML
*
* @throws IOException
*/
private void getXML() {
try {
// 1-获取解析器
SAXBuilder dBuilder = new SAXBuilder();
// 2-获取xml文件的流
InputStream is = JDomXML.class.getClassLoader().getResourceAsStream("Request.xml");
// 3- 解析XML文件
Document doc = dBuilder.build(is);
// 4-获取元素
// 4-1 获取根元素<request>
Element root = doc.getRootElement();// 拿到request
// 4-2获取子元素<appnt>
Element appntEle = root.getChild("appnt");
// 4-2-1 获取<appnt>的<name>元素
Element appntName = appntEle.getChild("name");
logger.debug("<name>元素名称: " + appntName.getName());
logger.debug("<name>元素文本信息: " + appntName.getText());
// 4-2-2 获取<appnt>的<age>元素
Element ageName = appntEle.getChild("age");
logger.debug("ageName: " + ageName.getName());
logger.debug("ageText: " + ageName.getText());
// 4-3 获取子元素<insured>
Element insuredEle = root.getChild("insured");
// 4-3-1 获取<insured>的name属性
Attribute nameAttr = insuredEle.getAttribute("name");
logger.debug("nameAttr: " + nameAttr.getValue());
// 4-3-2 获取<insured>的age属性
Attribute ageAttr = insuredEle.getAttribute("age");
logger.debug("ageAttr: " + ageAttr.getValue());
logger.debug("直接拿ageAttr: " + insuredEle.getAttributeValue("age"));
// 4-4 获取子元素<product>
Element productEle = root.getChild("product");
// 4-4 获取子元素<Service>
List<Element> serviceLists = root.getChildren("Service");// 拿到<Service>元素
for (Element servlce : serviceLists) {
// 获取<Service>的两个属性: name和type
logger.debug(servlce.getAttributeValue("name"));
logger.debug(servlce.getAttributeValue("type"));
// 获取<Service>的子元素 <RequestHandle>,可以有多个
List<Element> requestLists = servlce.getChildren("RequestHandle");
for (Element Request : requestLists) {
// 获取 <RequestHandle>的子元素 <Handle>,可以多个
List<Element> handleLists = Request.getChildren("Handle");
for (Element handle : handleLists) {
// 获取<Handle>的属性 : name 和 type
logger.debug(handle.getAttributeValue("name"));
logger.debug(handle.getAttributeValue("type"));
// 获取<Handle> 的子元素<init-param> 可以有多个
List<Element> paramLists = handle.getChildren("init-param");
for (Element init_param : paramLists) {
// 获取<init-param>的两个子元素 <param-name>和<param-value>
Element param_name = init_param.getChild("param-name");
logger.debug("param-name: " + param_name.getTextTrim());
Element param_value = init_param.getChild("param-value");
logger.debug("param-value: " + param_value.getTextTrim());
}
}
}
}
} catch (JDOMException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
二、Dom4j
1、简介:
1)Dom4j是以上各api中效率最好的,javm内部使用的也是Dom4j。
2)Dom4j的文档写的非常好,介绍的很详细。它的文档位于下载包下:如dom4j-1.6.1\docs\guide.html 。
3)使用Dom4j解析XML文档,可以使用强大XPath语法。语法地址:http://www.zvon.org/xxl/XPathTutorial/General_chi/examples.html
2、创建XML文档以及对其CRUD
1)Dom4j创建Document和Element都需要用到 DocumentHelper 类。如
// 创建一篇文档
Document doc = DocumentHelper.createDocument();
// 创建元素
Element ele = DocumentHelper.createElement("插入新元素");2)Dom4j的语法设计,使得链式编程得到很大的便利。如添加一个新元素:
// 3-在<insured>下添加新元素,注意,addText\addAttribute都是操作在元素<DESC>上
document.getRootElement().element("insured").addElement("DESC").addText("新加的元素").addAttribute("属性", "值");
3)Dom4j的解析语法有一个很大的不足,就是作CRUD的时候必须一层一层的从根元素开始获取。(可以使用XPath语法解决)。如下面添加、删除、更新元素时,必须从根节点开始获取,然后操作:
// 3-2 指定位置上添加新元素:如<product>元素下子元素 <price>下添加<插入新元素>
// 这个插入很恶心,一点都不爽。
Element product = document.getRootElement().element("product");
List<Element> list = product.elements();
list.add(2, DocumentHelper.createElement("插入新元素").addText("这个插入很恶心"));
// 3-3 删除指定节点,如删除<appnt>的<age>子元素
Element ageEle = document.getRootElement().element("appnt").element("age");//得到<age>元素
// ageEle.remove(ageEle);//是否可以删除自己?? 答案是不行
ageEle.getParent().remove(ageEle);//只能利用父亲元素的remove()方法
// 3-4 更新 修改<appnt>的<name>子元素
document.getRootElement().element("appnt").element("name").addAttribute("修改属性", "修改的属性值").setText("修改文本域:SAM");
4)生成文档的输出:
①、尽量配合格式化 OutputFormat 类一起使用,这样可以避免编码错误的发生。
②、使用字符流转码,如:new OutputStreamWriter(new FileOutputStream("resources/Request.xml"), "utf-8")
③、直接使用字节流配合 OutputFormat 格式化一起使用。
// 4-输出到文档
// 注意:这边有可能会有编码问题:其实用于读取写入sting化的xml文件(称为报文)时,都存在转码问题。
// 以前有个项目,和天猫对接,天猫的报文编码为 GBK 的,我们的系统统一UTF-8.这样很明显需要转码。
// 先用 GBK 读取后需要转成 UTF-8 才能使用。
// 问题分析:
// 1) 这边控制编码的是IO流的 FileWriter 。
// 2) FileWriter默认会使用本地平台的码表,如你的平台编码为GB2312。而你的xml文档的编码为utf-8,这样写入中文会乱码,即写入的为GB2312 。
// 3) XMLWriter这边只接受 Writer ,我们可以使用 OutputStreamWriter 指定写入的编码为utf-8即可。
OutputFormat format = OutputFormat.createPrettyPrint();// 格式化编辑器
XMLWriter writer = new XMLWriter(new FileWriter("resources/Request.xml"), format);
// XMLWriter writer = new XMLWriter(new OutputStreamWriter(new FileOutputStream("resources/Request.xml"), "utf-8"));
writer.write(document);
writer.close();
// 编码扩展:修改xml的编码为 GBK 。
// document这边默认会转成utf-8,写出的时候需要指定格式化输出器的编码为 GBK,
// 然后利用字节流 FileOutputStream 直接输出
// 或者利用字节流转成字符流 OutputStreamWriter,但是需要指定转的编码
// OutputFormat format = OutputFormat.createPrettyPrint();//格式化编辑器
// format.setEncoding("GBK");
// 字符流
// XMLWriter writer = new XMLWriter(new OutputStreamWriter(new FileOutputStream("resources/Request.xml"), "GBK"),format);
// 自节流
// XMLWriter writer = new XMLWriter(new FileOutputStream("resources/Request.xml"), format);
// writer.write(document);
// writer.close();
// 漂亮的格式输出到控制台
OutputFormat format2 = OutputFormat.createPrettyPrint();
// format2.setEncoding("GBK");
writer = new XMLWriter(System.out, format2);
writer.write(document);
5)其实Dom4j的语法,在文档中非常详细,这边就不多说了。注意输出编码问题即可。详细代码如下:
/**
* 创建XML文档以及CRUD
*
* @throws DocumentException
* @throws IOException
*/
public void createDocument() {
/******* 在原有文档上添加 **********/
try {
// 1- 获取解析器
SAXReader reader = new SAXReader();
// 2-读取文档
Document document = reader.read(new File("resources/Request.xml"));
// Document document = reader.read(new InputStreamReader(new FileInputStream("resources/Request.xml"),"GBK"));
// 3-在<insured>下添加新元素,注意,addText\addAttribute都是操作在元素<DESC>上
document.getRootElement().element("insured").addElement("DESC").addText("新加的元素").addAttribute("属性", "值");
// 3-2 指定位置上添加新元素:如<product>元素下子元素 <price>下添加<插入新元素>
// 这个插入很恶心,一点都不爽。
Element product = document.getRootElement().element("product");
List<Element> list = product.elements();
list.add(2, DocumentHelper.createElement("插入新元素").addText("这个插入很恶心"));
// 3-3 删除指定节点,如删除<appnt>的<age>子元素
Element ageEle = document.getRootElement().element("appnt").element("age");//得到<age>元素
// ageEle.remove(ageEle);//是否一删除自己?? 答案是不行
ageEle.getParent().remove(ageEle);//只能利用父亲元素的remove()方法
// 3-4 更新 修改<appnt>的<name>子元素
document.getRootElement().element("appnt").element("name").addAttribute("修改属性", "修改的属性值").setText("修改文本域:SAM");
// 4-输出到文档
// 注意:这边有可能会有编码问题:其实用于读取写入sting化的xml文件(称为报文)时,都存在转码问题。
// 以前有个项目,和天猫对接,天猫的报文编码为 GBK 的,我们的系统统一UTF-8.这样很明显需要转码。
// 先用 GBK 读取后需要转成 UTF-8 才能使用。
// 问题分析:
// 1) 这边控制编码的是IO流的 FileWriter 。
// 2) FileWriter默认会使用本地平台的码表,如你的平台编码为GB2312。而你的xml文档的编码为utf-8,这样写入中文会乱码,即写入的为GB2312 。
// 3) XMLWriter这边只接受 Writer ,我们可以使用 OutputStreamWriter 指定写入的编码为utf-8即可。
OutputFormat format = OutputFormat.createPrettyPrint();// 格式化编辑器
XMLWriter writer = new XMLWriter(new FileWriter("resources/Request.xml"), format);
// XMLWriter writer = new XMLWriter(new OutputStreamWriter(new FileOutputStream("resources/Request.xml"), "utf-8"));
writer.write(document);
writer.close();
// 编码扩展:修改xml的编码为 GBK 。
// document这边默认会转成utf-8,写出的时候需要指定格式化输出器的编码为 GBK,
// 然后利用字节流 FileOutputStream 直接输出
// 或者利用字节流转成字符流 OutputStreamWriter,但是需要指定转的编码
// OutputFormat format = OutputFormat.createPrettyPrint();//格式化编辑器
// format.setEncoding("GBK");
// 字符流
// XMLWriter writer = new XMLWriter(new OutputStreamWriter(new FileOutputStream("resources/Request.xml"), "GBK"),format);
// 自节流
// XMLWriter writer = new XMLWriter(new FileOutputStream("resources/Request.xml"), format);
// writer.write(document);
// writer.close();
// 漂亮的格式输出到控制台
OutputFormat format2 = OutputFormat.createPrettyPrint();
// format2.setEncoding("GBK");
writer = new XMLWriter(System.out, format2);
writer.write(document);
/******************** 创建新文档 ********************/
// 创建一篇文档
Document doc = DocumentHelper.createDocument();
// 添加一个元素
Element root = doc.addElement("catalog");
// 为root元素添加注释
root.addComment("An XML Catalog");
// 添加标记
root.addProcessingInstruction("target", "instruction");
// 创建元素
Element journalEl = new BaseElement("journal");
// 添加属性
journalEl.addAttribute("title", "XML Zone");
journalEl.addAttribute("publisher", "IBM developerWorks");
root.add(journalEl);
// 添加元素
Element articleEl = journalEl.addElement("article");
articleEl.addAttribute("level", "Intermediate");
articleEl.addAttribute("date", "December-2001");
Element titleEl = articleEl.addElement("title");
// 设置文本内容
titleEl.setText("Java configuration with XML Schema");
// titleEl.addText("Java configuration with XML Schema");
Element authorEl = articleEl.addElement("author");
authorEl.addElement("firstname").setText("Marcello");
authorEl.addElement("lastname").addText("Vitaletti");
// 可以使用 addDocType() 方法添加文档类型说明。
doc.addDocType("catalog", null, "file://c:/Dtds/catalog.dtd");
// 将xml转换成文本
doc.asXML();
OutputFormat format3 = OutputFormat.createPrettyPrint();
writer = new XMLWriter(System.out, format3);
writer.write(doc);
} catch (DocumentException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}3、使用Dom4j的语法解析XML文档。一般来说,比较麻烦,使用xpath更加简便。
/**
* 使用Dom4j解析xml文档
*/
public void readXml() {
try {
// 1- 获取解析器
SAXReader reader = new SAXReader();
// 2-读取文档
InputStream in = this.getClass().getClassLoader().getResourceAsStream("Request.xml");
Document document = reader.read(in);
// 3-读取根节点
Element root = document.getRootElement();
QName qName = root.getQName();
String name = qName.getName();
logger.debug("根节点的名称: " + name);
name = root.getName();
logger.debug("根节点的名称2 : " + name);
// 4-获取子节点
Element appneEle = root.element("appnt");// 只有一个
String appntName = appneEle.element("name").getTextTrim();
String appntAge = appneEle.element("age").getTextTrim();
logger.debug("appnt节点的name为 : " + appntName + " |age为 " + appntAge);
// 5-获取有多个子节点
Element serviceEle = (Element) root.elements("Service").get(1);
// 5-1 获取属性值
String attrName = serviceEle.attribute("name").getName();
String attrNameValue = serviceEle.attribute("name").getValue();
logger.debug("第一个<Service>元素的的name属性名:为 : " + attrName + " |值为 " + attrNameValue);
String attrTypeValue = serviceEle.attributeValue("type");
logger.debug("第一个<Service>元素的的type属性值为 " + attrTypeValue);
// 5-2 获取多个同名子节点
List<Element> serviceEles = root.elements("Service");
for (Iterator<Element> iterator = serviceEles.iterator(); iterator.hasNext();) {
Element element = (Element) iterator.next();
// 递归
treeWalk(element);
// ...随意操作
}
} catch (DocumentException e) {
e.printStackTrace();
}
}
/**
* 递归方法
*
* @param element
*/
public void treeWalk(Element element) {
System.out.println(element.getName());
for (int i = 0, size = element.nodeCount(); i < size; i++) {
Node node = element.node(i);
if (node instanceof Element) {
treeWalk((Element) node);
} else {
// do something....
}
}
}
4、XML与String的相互转换:
1)XML转String:Document.asXML()方法即可。
2)String转XML:需要使用DocumentHelper.parseText("")方法。
/**
* XML与String的相互转换
*/
public void ConverXmlAndString() {
try {
Document document = DocumentHelper.createDocument();
String text = document.asXML();
String text2 = "<person> <name>James</name> </person>";
Document document2 = DocumentHelper.parseText(text2);
} catch (DocumentException e) {
e.printStackTrace();
}
}
5、使用XPath解析XML文档。
1)必须添加 jaxen.jar 包,在下载Dom4j包中就有,添加即可。
2)XPath的语法规范有中文版本,具体地址为:http://www.zvon.org/xxl/XPathTutorial/General_chi/examples.html
3)Dom4j中与XPath有关的主要方法为
//得到单个元素,如果为多个,默认取第一个
document.selectSingleNode("");
//得到符合条件的元素集合
document.selectNodes("");
5)具体语法代码如下:
<?xml version="1.0" encoding="UTF-8"?> <rootElement name="属性"> <user id="a1" name="33">用户1</user> <user id="a2" name="33">用户2</user> <APPNT id="1" name="SAM-SHO" age="28">我是投保人<!--我是注解--></APPNT> <INSURED id="2" name="SAMMY" age="49"><user id="a3">用户3</user></INSURED> </rootElement>
/**
* 利用强大的XPath 语法:http://www.zvon.org/xxl/XPathTutorial/General/examples.html
* 需要导入jaxen.jar包
*/
public void xPathTest() {
try {
// 1- 获取解析器
SAXReader reader = new SAXReader();
// 2-读取文档
Document document = reader.read(new File("resources/xpath.xml"));
// 3-利用XPath
// 3-1 以斜线 / 开始, 那么该路径就表示到一个元素的绝对路径
Element rootEle = (Element) document.selectSingleNode("/rootElement");// 得到根元素<rootElement>元素
System.out.println(rootEle.attributeValue("name"));// 属性
// 3-2 得到<APPNT>元素: "/"绝对路径,层次必须清楚
Node appntNode = document.selectSingleNode("/rootElement/APPNT");
System.out.println(appntNode.getText());// 我是投保人
// 3-3 双斜线 // 开头, 则表示选择文档中所有满足双斜线//之后规则的元素,相对路径
// 得到所有的user元素,不论层级,"用户3"也会在范围内
List<Node> list = document.selectNodes("//user");
for (Iterator<Node> iterator = list.iterator(); iterator.hasNext();) {
Node node = (Node) iterator.next();
System.out.println(node.getText());
}
// 3-4 删选元素:"@",如id='a1'的<user>元素,需要有单引号''
Node userNode = document.selectSingleNode("//user[@id='a1']");
System.out.println(userNode.getText());// 用户1
// @ 中可以使用 and 和or等逻辑:如 获取id='a1'或者id='a3'的<user>元素
List<Node> listNodes = document.selectNodes("//user[@id='a1' or @id='a3']");
for (Iterator<Node> iterator = listNodes.iterator(); iterator.hasNext();) {
Node node = (Node) iterator.next();
System.out.println("获取id='a1'或者id='a3'的<user>元素 : "+node.getText());
}
// @:获取id='a1'and name='33'的<user>元素
userNode = document.selectSingleNode("//user[@id='a1' and @name='33']");
System.out.println("获取id='a1'and name='33'的<user>元素: " + userNode.getText());
} catch (DocumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
三、小结:
1、jaxp的api使用还是比较麻烦的,功能和效率都不是很好,直接可以被JDom和Dom4J代替。意见是如果不是万不得已,还是不要使用。
2、Dom4j 比 JDom 的使用更加简便,且效率更高。
2、其实Java 内部的 jaxm 用的就是 Dom4j,所以一般我们使用Dom4j即可。而在解析xml时推荐使用更加强大简便的XPath语法。
XML学习笔记(五):使用 jdom和dom4j 解析XML
标签:jdom dom4j xml解析 xpath语法 jaxp
原文地址:http://blog.csdn.net/u012228718/article/details/41112991