项目任务里需要解析xml报文。
于是开始着手学习相关知识,在查看了多篇博文后找到了一篇不错的,讲的很实用。
转载来源:Java开发笔记(一百零九)XML报文的定义和解析
以下为转载的正文,略微删改文本
虽然json串短小精悍,也能有效表达层次结构,但是每个元素只能找到对应的元素值,不能体现更丰富的样式特征。比如某个元素除了要传输它的字符串文本,还想传输该文本的类型、字体大小、字体颜色等特征,且这些额外的风格样式与业务逻辑无关,自然不适合为它们单独设立参数字段。
倘若采用JSON格式定义包括样式特征在内的文本元素,要么摒弃风格样式这种附加属性,要么将风格样式单列为专门的字段参数,然而不管哪种做法,都未能妥善解决附加属性的表达问题。
可见轻量级的JSON格式依然存在力不从心的情况,为此人们早早发明了拥有强大表示能力的XML格式,XML的全称是“Extensible Markup Language”(可扩展标记语言),它不但支持结构化数据的描述,还支持各类附加属性的定义,非常适合在网络中传输信息。
下面先看一个XML报文格式的购物订单样例:
<?xml version="1.0" encoding="gbk"?> <order> <user_info> <name type="string">思无邪</name> <address type="string">桃花岛水帘洞123号</address> <phone type="string">15960238696</phone> </user_info> <goods_list> <goods_item> <goods_name type="string">Mate30</goods_name> <goods_number type="int">1</goods_number> <goods_price type="double">8888</goods_price> </goods_item> <goods_item> <goods_name type="string">格力中央空调</goods_name> <goods_number type="int">1</goods_number> <goods_price type="double">58000</goods_price> </goods_item> <goods_item> <goods_name type="string">红蜻蜓皮鞋</goods_name> <goods_number type="int">3</goods_number> <goods_price type="double">999</goods_price> </goods_item> </goods_list> </order>
接着对上面的XML样例庖丁解牛,分析一下XML格式都有哪些特点,分析结果罗列如下:
<参数名称>参数值</参数名称>
。大致了解了XML报文的格式规范,还得在程序中加以解析才行。
传统的XML解析方式有DOM和SAX两种:
单就某个节点值的解析过程而言,加载所有节点的DOM方式显然较费功夫,从头顺序查找的SAX方式执行效率更高。但若要求同时获取多个节点的数值,则采取树状结构遍历的DOM方式总体性能更加,而每次都从头找起的SAX方式无疑做了重复劳动。总之两种方式的解析效果各有优劣,需要按照实际场景决定取舍。
尽管JDK集成了DOM与SAX的解析工具,其中DOM解析工具封装在包org.w3c.dom中,SAX解析工具封装在包javax.xml.parsers中,可是它俩用起来着实费劲,解析过程艰深晦涩,实际开发当中基本不予采用。应用比较多的XML解析工具反而是第三方的Dom4j,Dom4j的解析方式遵循DOM规则,但比起Java自带的DOM工具要易用得多,其性能也很优异,几乎成为Java开发必备的XML解析神器了。通过Dom4j解析XML报文的步骤主要有下列五步:
在具体的节点解析过程之中,会频繁调用Element的相关方法,它的常用方法说明如下:
仍以前述的XML报文为例,下面是采用Dom4j解析该XML串的代码例子:
// 通过dom4j解析xml串 private static GoodsOrder testParserByDom4j(String xml) { GoodsOrder order = new GoodsOrder(); // 创建一个购物订单对象 // 创建SAXReader阅读器对象 SAXReader reader = new SAXReader(); // 根据字符串构建字节数组输入流 try (InputStream is = new ByteArrayInputStream(xml.getBytes(CHARSET))) { // 命令阅读器从输入流中读取文档对象 Document document = reader.read(is); // 获得文档对象的根节点 Element root = document.getRootElement(); // 获取根节点下面名叫user_info的节点 Element user_info = root.element("user_info"); // 获取user_info节点下面名叫name的节点值 order.user_info.name = user_info.element("name").getText(); // 获取user_info节点下面名叫address的节点值 order.user_info.address = user_info.element("address").getText(); // 获取user_info节点下面名叫phone的节点值 order.user_info.phone = user_info.element("phone").getText(); System.out.println(String.format("用户信息如下:姓名=%s,地址=%s,手机号=%s", order.user_info.name, order.user_info.address, order.user_info.phone)); // 获取根节点下面名叫goods_list的节点清单 List<Element> goods_list = root.element("goods_list").elements(); for (int i=0; i<goods_list.size(); i++) { // 遍历商品节点清单 Element goods_item = goods_list.get(i); GoodsItem item = new GoodsItem(); // 创建一项商品对象 // 获取当前商品项节点下面名叫goods_name的节点值 item.goods_name = goods_item.element("goods_name").getText(); // 获取当前商品项节点下面名叫goods_number的节点值 item.goods_number = Integer.parseInt(goods_item.element("goods_number").getText()); // 获取当前商品项节点下面名叫goods_price的节点值 item.goods_price = Double.parseDouble(goods_item.element("goods_price").getText()); System.out.println(String.format("第%d个商品:名称=%s,数量=%d,价格=%f", i+1, item.goods_name, item.goods_number, item.goods_price)); order.goods_list.add(item); // 往商品清单中添加指定商品对象 } } catch (Exception e) { e.printStackTrace(); } return order; // 返回解析后的购物订单对象 }
运行以上的解析代码,观察到以下的购物订单日志,可见成功实现了xml串到对象的解析操作:
用户信息如下:姓名=思无邪,地址=桃花岛水帘洞123号,手机号=15960238696
第1个商品:名称=Mate30,数量=1,价格=8888.000000
第2个商品:名称=格力中央空调,数量=1,价格=58000.000000
第3个商品:名称=红蜻蜓皮鞋,数量=3,价格=999.000000
除了解析各节点的节点值,Dom4j还能解析各节点的属性值,若想正常解析指定名称的属性值,则需明确下列三个要素:
有了这三个要素,即可通过以下方法从指定节点的指定属性成功获取属性值:
// 打印指定节点名称的指定属性值 private static void printValueAndAttr(Element parent, String node_name, String attr_name) { // 获取父节点下面指定名称的子节点 Element element = parent.element(node_name); // 获得子节点的节点值 String node_value = element.getText(); String attr_value = ""; // 根据属性名称获取子节点的对应属性对象 Attribute attr = element.attribute(attr_name); if (attr != null) { attr_value = attr.getText(); // 获取该属性的属性值 } // 打印子节点的详细信息,包括节点名称、节点值、属性名称、属性值 System.out.println(String.format("节点名称=%s, 节点值=%s, 属性名称=%s, 属性值=%s", node_name, node_value, attr_name, attr_value)); }
接下来在原先的XML解析代码里补充如下的一行属性解析代码:
// 打印user_info节点的name子节点的type属性值 printValueAndAttr(user_info, "name", "type");
再次运行XML解析代码,在输出的购物订单日志中观察到多了下面这行日志,表示解析到了name节点的type属性值:
节点名称=name, 节点值=思无邪, 属性名称=type, 属性值=string
很好用,转载一份