您的位置:首页 > 编程语言 > Java开发

[Java]利用DOM解析DOM文件|利用socket发送XML DOM

2017-05-29 16:00 253 查看
DOM的特点是随机读写查询,但是一次性要把xml读到内存里,对于大的xml文件而言,不是个好方法。

加之我个人觉得W3C的Document这套体系的坑比较多,如果要用的话,需要非常系统的学习才能避开这些坑,挺麻烦的。

所以我还是建议采用SAX或其他已包装好的API来写xml比较好。

看了一圈,感觉写得简洁、系统性的博客不多,大多嵌套了太多其他杂乱的相关知识。

但是这篇文章说得很是明白:http://www.cnblogs.com/shenliang123/archive/2012/05/11/2495252.html

>DOM下利用Java对XML进行解析(导包:javax.xml.parsers.*)

//1.获取工厂类
DocumentBuilderFactory dbf=DocumentBuilderFactory.newInstance();
//2.从工厂中得到一个解析器
DocumentBuilder db=dbf.newDocumentBuilder();
//3.利用解析器解析文件,获得document对象
Document doc=db.parse(new File(url));

利用parse方法获取Document共有以下几种方法:



关于Document与String互转,可以参考这里:http://kingxss.iteye.com/blog/1026954

利用第一个方法,可以由String构建一个DOM。关于InputSource(包:org.xml.sax.*)的问题,参见文末。

--------------------------

由于我们的目的只是为了获取Document,所以上面的代码可以更加的精简,把1、2步合成到一个地方去,所以整个步骤最后只有两步:

DocumentBuilder db=DocumentBuilderFactory.newInstance().newDocumentBuilder();//获取解析器
Document doc=db.parse(new File(url));//从文件解析成document文件


接下来,从这个document文件对象中获取每个字节点的列表,并对单个节点的数据进行输出(相当于解析获取数据):

//4.得到所有根部节的列表
NodeList list=doc.getElementsByTagName("employee");//或 doc.getChildNodes();
//5.遍历、解析输出等操作
for(int i=0;i<list.getLength();i++){
Node n=list.item(i);//假设这里用的是Node进行操作
if(n.getNodeType()==Node.ELEMENT_NODE){//坑1:判断是否为有效文本字符
System.out.println("\t"+n.getNodeName()+":"+n.getTextContent());//坑2:getNodeValue()方法输出均为null
}
}

注意到上面的代码我标注了两个坑,待会儿我再来说。

-----------------------

BTW:

有人可能要问,如果用Element进行操作,是不是就能避开空白文本的问题呢?比如:

for(int j=0;j<nlist.getLength();j++){
Element e=(Element)nlist.item(j);
//输出操作等,略
}
答案是不能。注意到我们上面是采用:
NodeList list=doc.getElementsByTagName("employee");//语句1

的方法获取节点列表的,也就意味着,空白节点(空格啥的)也进来了。此处进行Node->Element的强制转换很可能报错类型转换失败,处理方法有两种:

1.把语句1中的方法换成:getChildNodes()。

但这样其实十分不灵活,没法按标签获取。

2.添加判断,也就是:

Node info=nlist.item(j);
if(info.getNodeType()==Node.ELEMENT_NODE){
Element e=(Element) info;
}
但这样看着非常多此一举。

>利用socket发送xml文件

xml作为一种数据类型,肯定免不了在不同的服务器间进行传送。

但我个人建议还是采用json来进行这种数据交换(更轻量化、更快捷),或者至少,不要用DOM下的xml进行这种操作。

json的相关内容见此:

http://blog.csdn.net/shenpibaipao/article/details/71760226

但如果你执意要用DOM进行这个操作,好吧,接着往下看:

首先,我们知道socket发送的数据可以是字符串,或者随意什么字节流,那么我们只要把xml编译成一个string或是bytes[]就可以发送了。

关于socket的相关知识看此:

http://blog.csdn.net/shenpibaipao/article/details/70176038

至于转化和再编成document文件,代码见下:

//转化为xml-string便于用socket发送
Transformer tr=TransformerFactory.newInstance().newTransformer();//获得一个转化器
tr.setOutputProperty("encoding","UTF-8");//设置编码格式
ByteArrayOutputStream  bos  =  new  ByteArrayOutputStream();
tr.transform(new DOMSource(doc), new StreamResult(bos));//bos就是要发送的流
//再编码
InputStream is= new ByteArrayInputStream(bos.toString().getBytes());
Document doc2=db.parse(is);//doc2就是接收方获取到的数据再编码成document的

这里还有一个坑,是关于InputSource()方法的,称其为坑3,留到下面再说。

至此,具体应用部分讲完了。后续会考虑更新DOM下的xml“改删加”操作,以及SAX的解析。

>神坑

(下面这部分是基础知识的内容,如果只是偏向应用,请直接无视,等遇上问题了再来看)

其一:
n.getNodeType()==Node.ELEMENT_NODE


写这一行的目的是为了判断这个节点是否是有效的标签节点,也就是:<tag>value</tag>。

因为空白字符(空格换行制表符等)并不会被默认丢弃,而是被单做一个有效文本,所以如果不进行这一行的判定,你很可能System.out.plintln(...)出来的字符串会是:#text

-----------

其二:
getNodeValue()方法输出均为null


肯定不止我一个人注意到除了getTextContent方法,还有一个名字更为蛊惑的getNodeValue,要是一没注意,铁定就着道了。

为什么getNodeValue输出的都是null呢?

因为Dom解析器中Node和Element是一个内含的关系,Node指的是哪些呢?

答案是任何,一个空格,一个标签,一个Element都可以是一个Node。

而Element指的是<tag>value</tag>这样的东西。(可以认为Element属于Node)

而Node的getNodeValue只有对是Element的Node的内容才有意义。

所以我其实非常无语这个方法的原始设定,感觉除非是什么特殊用途,不然没啥能用得上的地方,还容易造成歧义。

这里可看进一步研究其差异:

http://blog.csdn.net/qq_19457117/article/details/51137415

-----------

其三:
DocumentBuilder.parse(InputSource is)方法无效

这个真的是神坑。明明有这个方法,但是无论怎么输入都是不对的:

StringReader sr = new StringReader(bos.toString());
InputSource is=new InputSource(sr);
Document doc2=db.parse(is);//此处编译不通过,如下图:




原因是什么呢?其实这是个抽象方法:



且这个InputSource是属于包:org.xml.sax.InputSource,导错包就会导致参数不匹配。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息