JDK 自带dom 解析xml时 做的一些总结。
前言
xml的读取
先看一下一个简单的xml 格式
<root>
<a id='1' cc='2' >
<b>ss</b>
</a>
<c>s2</c>
<d>
<![CDATA[<e><f>f1</f></e>]]>
</d>
</root>
可以看出这个是一个常见的xml格式了。
针对dom解析来说。
每一个出现的节点都是一个元素就是Node 对象
举例 root 节点是一个Node对象。
其中a b c d也是一个Node对象 。
而且里面的值也是一个node对象 ss ,s2, cdata中的内容,
只是其中在Node对象中。他们的类型不一样。
要先了解一下Node对象中的元素类型了 所谓的element type
看下Node源码:摘抄出来坐下简单的注释
NodeType 类型介绍
// NodeType
/**
* 元素节点 可以被get 到子元素
*/
public static final short ELEMENT_NODE = 1;
/**
* 属性 从node中 get attribute 获取 获取一个 node集合在迭代获取此类型
*/
public static final short ATTRIBUTE_NODE = 2;
/**
*文本内容通过get value获取。 类型1 获取value 为null 必须获取内容才行
*/
public static final short TEXT_NODE = 3;
/**
* CDATA 类型数据
*/
public static final short CDATA_SECTION_NODE = 4;
/**
* The node is an <code>EntityReference</code>.
*/
public static final short ENTITY_REFERENCE_NODE = 5;
/**
* The node is an <code>Entity</code>.
*/
public static final short ENTITY_NODE = 6;
/**
* The node is a <code>ProcessingInstruction</code>.
*/
public static final short PROCESSING_INSTRUCTION_NODE = 7;
/**
* 注释类型
*/
public static final short COMMENT_NODE = 8;
/**
*
*/
public static final short DOCUMENT_NODE = 9;
/**
* document 描述类型节点
*/
public static final short DOCUMENT_TYPE_NODE = 10;
/**
* The node is a <code>DocumentFragment</code>.
*/
public static final short DOCUMENT_FRAGMENT_NODE = 11;
/**
* The node is a <code>Notation</code>.
*/
public static final short NOTATION_NODE = 12;
ELEMENT_NODE 类型
上代码过程吧 ,一步一步了解
public static void main(String[] args) {
// 1.创建DocumentBuilderFactory对象
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// 2.创建DocumentBuilder对象
try {
DocumentBuilder builder = factory.newDocumentBuilder();
Document d = builder.parse(new ByteArrayInputStream(xml2.getBytes()));
renshiNodeType(d.getChildNodes());
} catch (Exception e) {
e.printStackTrace();
}
}
public static void renshiNodeType(NodeList list) {
for(int i=0;i<list.getLength();i++)
{
Node _n = list.item(i);
log.info("{} {} {}",_n.getNodeType(),_n.getNodeName(),_n.getNodeValue());
}
}
static String xml2 = "<root><a>sss</a></root>";
输出结果
[main] INFO org.cmcc.ecip.examples.xml.Dom - 1 root null
说明这个root 对象是一个Node对象但是取不到他的值。
TEXT_NODE
改造一下在循环,修改方法renshiNodeType
public static void renshiNodeType(NodeList list) {
for(int i=0;i<list.getLength();i++)
{
Node _n = list.item(i);
log.info("{} {} {}",_n.getNodeType(),_n.getNodeName(),_n.getNodeValue());
if(_n.getNodeType() == 1) {
renshiNodeType(_n.getChildNodes());
}
}
}
运行后得到了结果
[main] INFO org.cmcc.ecip.examples.xml.Dom - 1 root null
[main] INFO org.cmcc.ecip.examples.xml.Dom - 1 a null
[main] INFO org.cmcc.ecip.examples.xml.Dom - 3 #text sss
可以看到了这个最简单的xml 对象中sss值作为 type为3的结果输出了出来
CDATA_SECTION_NODE
我们用这个方法输出一下开始定义的xml格式结果
[main] INFO org.cmcc.ecip.examples.xml.Dom - node >> 1 root null
[main] INFO org.cmcc.ecip.examples.xml.Dom - node >> 1 a null
[main] INFO org.cmcc.ecip.examples.xml.Dom - node >> 1 b null
[main] INFO org.cmcc.ecip.examples.xml.Dom - node >> 3 #text ss
[main] INFO org.cmcc.ecip.examples.xml.Dom - node >> 1 c null
[main] INFO org.cmcc.ecip.examples.xml.Dom - node >> 3 #text s2
[main] INFO org.cmcc.ecip.examples.xml.Dom - node >> 1 d null
[main] INFO org.cmcc.ecip.examples.xml.Dom - node >> 4 #cdata-section <e><f>f1</f></e>
可以看出来 cdata 被搞出来了 是类型4
ATTRIBUTE_NODE
但是对于
<a id='1' cc='2' >
中的id 和cc 没有搞出来。
这是需要修改一下方法增加一个新的循环内容 针对attribute的
public static void renshiNodeType(NodeList list) {
for (int i = 0; i < list.getLength(); i++) {
Node _n = list.item(i);
log.info("node >> {} {} {}", _n.getNodeType(), _n.getNodeName(), _n.getNodeValue());
NamedNodeMap map = _n.getAttributes();
for (int j = 0; map != null && j < map.getLength(); j++) {
Node an = map.item(j);
log.info("attr >> {} {} {}", an.getNodeType(), an.getNodeName(), an.getNodeValue());
}
if (_n.getNodeType() == 1) {
renshiNodeType(_n.getChildNodes());
}
}
}
在运行以后,看结果
[main] INFO org.cmcc.ecip.examples.xml.Dom - node >> 1 root null
[main] INFO org.cmcc.ecip.examples.xml.Dom - node >> 1 a null
[main] INFO org.cmcc.ecip.examples.xml.Dom - attr >> 2 cc 2
[main] INFO org.cmcc.ecip.examples.xml.Dom - attr >> 2 id 1
[main] INFO org.cmcc.ecip.examples.xml.Dom - node >> 1 b null
[main] INFO org.cmcc.ecip.examples.xml.Dom - node >> 3 #text ss
[main] INFO org.cmcc.ecip.examples.xml.Dom - node >> 1 c null
[main] INFO org.cmcc.ecip.examples.xml.Dom - node >> 3 #text s2
[main] INFO org.cmcc.ecip.examples.xml.Dom - node >> 1 d null
[main] INFO org.cmcc.ecip.examples.xml.Dom - node >> 4 #cdata-section <e><f>f1</f></e>
id 和cc 的type被搞出来了。是2
其实到这里非常常用的类型就已经搞出来了。
COMMENT_NODE & DOCUMENT_TYPE_NODE
但是还有其他几种类型不知道怎么搞。简单改了一下xml看看一下xml就知道了。
<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<!-- this is comment -->"
<!DOCTYPE root[<!ELEMENT users (a+)>]>
<root><a>sss</a></root>
对这个 xml运行一下结果
[main] INFO org.cmcc.ecip.examples.xml.Dom - node >> 8 #comment this is comment null
[main] INFO org.cmcc.ecip.examples.xml.Dom - node >> 10 root null null
[main] INFO org.cmcc.ecip.examples.xml.Dom - node >> 1 root null null
[main] INFO org.cmcc.ecip.examples.xml.Dom - node >> 1 a null null
[main] INFO org.cmcc.ecip.examples.xml.Dom - node >> 3 #text sss null
显而易见了。
总结一下
关于读取xml
对于几种常用类型的解析 需要用到的方法有
getNodeType 获取类型
getNodeName 获取节点名称 当为Text 类型为 name 就是#text 对于 comment 类型也是这样
getNodeValue 只有text类型获取有返回值。要是其他类型返回null
xml的创建于生成
看一个简单的例子
public static void main(String a[]) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.newDocument();
// 创建各个节点并相互进行append为树形结果。
//对比文章上半部分看好创建的方法名称与 实际类型
Element rootNode = document.createElement("root");
Element aa1 = document.createElement("aa1");
Attr a3 = document.createAttribute("a3");
a3.setNodeValue("a31");
a3.setTextContent("a32");
a3.setValue("a33");
Attr a4 = document.createAttributeNS("http://namespce.test2","cc:a4");
a4.setNodeValue("a41");
a4.setTextContent("a42");
a4.setValue("a43");
aa1.setAttributeNode(a3);
aa1.setAttributeNode(a4);
Element a2 = document.createElementNS("http://namespce.test1", "dd:a2");
Text a2_v=document.createTextNode("a2 value");
a2.appendChild(a2_v);
aa1.appendChild(a2);
rootNode.appendChild(aa1);
CDATASection a5 =document.createCDATASection("<s>sss</s>");
EntityReference a6= document.createEntityReference("sdddd");
Text a7=document.createTextNode("test hello..");
Element aa2 = document.createElement("aa2");
aa2.appendChild(a5);
aa2.appendChild(a6);
aa2.appendChild(a7);
rootNode.appendChild(aa2);
document.appendChild(rootNode);
// 创建完成了进行输出
TransformerFactory tff = TransformerFactory.newInstance();
//这个是显示格式化用的
Transformer tf = tff.newTransformer();
tf.setOutputProperty(OutputKeys.INDENT, "yes");
tf.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
//输出。这里用到控制台了。所以用了system.out
tf.transform(new DOMSource(document), new StreamResult(System.out));
}
执行结果
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<root>
<aa1 a3="a33" xmlns:cc="http://namespce.test2" cc:a4="a43">
<dd:a2 xmlns:dd="http://namespce.test1">a2 value</dd:a2>
</aa1>
<aa2><![CDATA[<s>sss</s>]]>test hello..</aa2>
</root>
总结一下
对于dom创建xml 需要先构建一个 document 对象。再通过document创建需要的各种元素。创建完成后需要进行树形的构造。一个节点挂载到另外一个节点中去。就是appendChild。
关于dom创建时候关于头部声明 standalone 需要去掉的话 加上如下代码
// 去掉头部声明 standalone="no"
document.setXmlStandalone(true);