详解Java中Xml报文四种解析方式
xsobi 2025-01-11 18:15 1 浏览
为什么要了解xml报文的解析方式呢?我觉得有两种情况:
- 项目需求,比如接口调用时,接收到的数据是xml报文,这时候你需要解析xml报文,然后将其封装成对应的实体类,最后将报文中的数据写入库中。
- 有利于阅读框架源码,在Java众多优秀的开源框架中,有许多框架用到xml报文解析,比如,强大的Spring(虽然后来官方推荐采用注解的方式创建对象),Mybatis框架(解析Mybatis-config.xml和对应的XxxMapper.xml文件)等。
那么xml报文有哪些解析方式呢?答案有4种,前两种与平台无关,后两种是在Java环境中封装前两种方式,使调用者更加方便地使用。
- DOM解析
- SAX解析
- JDOM解析
- DOM4J解析
下面,分别介绍这4种解析方式的特点。
一、DOM解析
DOM(Document Object Model)文档对象模型,它将xml报文解析成一棵节点树结构。这种解析方式比较占用内存,但是它能够随机访问xml报文的节点,比较灵活。
优点:
- 将xml文档转换为树形结构,易于理解
- 解析过程中,熟悉结构在内存中保存,易于修改
缺点:
- 一次性读取整个xml报文,占用内存,对机器性能要求较高
>代码逻辑实现:采用工厂模式创建实例对象
基本流程:
- 实例化DocumentBuilderFactory对象
- 调用newDocumentBuilder()方法生成DocumentBuilder对象
- 调用parse()方法,解析xml,生成Document对象
- 用Document对象处理节点数据
1. xml报文:
<?xml version="1.0" encoding="UTF-8" ?>
<father id="张大">
<child id="张三">
<age value = "18"/>
<height>172</height>
</child>
<child id="张四">
<age value="19"/>
<height>173</height>
</child>
</father>
2. 代码举例:
public class DOMResolve {
private static final InputStream in;
static {
in = DOMResolve.class.getClassLoader().getResourceAsStream("test.xml");
if (in != null) {
System.out.println("加载成功...");
}
}
public static void main(String[] args) {
// 定义一个工厂API,该API使应用程序能够获取从XML文档生成DOM对象树的解析器
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
try {
// 通过DocumentBuilderFactory生成DocumentBuilder对象,该对象通过加载xml文件生成Document对象
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
// 调用parse()方法,解析xml,生成Document对象
Document document = documentBuilder.parse(in);
// 获取父节点father
NodeList father = document.getElementsByTagName("father");
if (father.getLength() != 1) {
throw new RuntimeException("xml报文父节点为空或多于1个");
}
Node item = father.item(0);
System.out.println("父节点:" + item.getNodeName());
NamedNodeMap attributes1 = item.getAttributes();
for (int i = 0; i < attributes1.getLength(); i++) {
Node attr = attributes1.item(i);
// 属性名、 属性值
System.out.println(attr.getNodeName() + " = " + attr.getNodeValue());
}
// 获取指定的子节点child
NodeList childNodes = document.getElementsByTagName("child");
for (int i = 0; i < childNodes.getLength(); i++) {
Node node = childNodes.item(i);
System.out.println("\t二级节点:" + node.getNodeName());
// 获取子节点的属性
NamedNodeMap attributes = node.getAttributes();
for (int j = 0; j < attributes.getLength(); j++) {
Node node1 = attributes.item(j);
System.out.print("\t\t" + node1.getNodeName() + "=" + node1.getNodeValue());
}
// 如果有子标签
if (node.hasChildNodes()) {
// 获取子节点下面的所有子标签
NodeList nodeChildNodes = node.getChildNodes();
for (int j = 0; j < nodeChildNodes.getLength(); j++) {
Node item2 = nodeChildNodes.item(j);
// 如果标签类型为Element
if (item2.getNodeType() == Node.ELEMENT_NODE) {
// 如果有属性标签,则打印出来
if (item2.hasAttributes()) {
NamedNodeMap attributes2 = item2.getAttributes();
for (int k = 0; k < attributes2.getLength(); k++) {
Node item1 = attributes2.item(0);
System.out.print("\t" + item1.getNodeName() + "=" + item1.getNodeValue());
}
System.out.println();
}
System.out.println("\t\t\t子节点=" + item2.getNodeName() + ", content = " + item2.getTextContent().trim());
}
}
}
}
} catch (ParserConfigurationException | SAXException | IOException e) {
e.printStackTrace();
}
}
}
二、SAX解析
SAX(Simple APIs for Xml) 简单的XML操作API,它是基于事件驱动的顺序访问模式,从头到尾逐个元素读取内容。
优点:
- 基于事件驱动,消耗内存小
缺点:
- 编码麻烦,需要重写DefaultHandler类的方法
- xml报文
<?xml version="1.0" encoding="UTF-8"?>
<father id="张大">
<child id="1">
<age>18</age>
<name>jack</name>
</child>
<child id="2">
<age>20</age>
<name>michael</name>
</child>
</father>
- 自定义Handler,该Handler继承DefaultHandler
public class ChildHandler extends DefaultHandler {
/**
* startDocument开始索引
*/
private static int startIndex = 0;
/**
* endDocument结束所有
*/
private static int endIndex = 0;
/**
* 当前解析到的节点名称
*/
private String currentTag;
/**
* 当前的节点对象
*/
private Child child;
/**
* 文档集合
*/
private List<Child> list;
public List<Child> getList() {
return list;
}
/**
* 文档解析时开始调用,该方法只会被调用一次
*
* @throws SAXException
*/
@Override
public void startDocument() throws SAXException {
super.startDocument();
list = new ArrayList<>();
startIndex = startIndex + 1;
System.out.println("xml文档解析开始,调用次数:" + startIndex);
}
/**
* 标签开始解析时调用
*
* @param uri xml文档的命名空间
* @param localName 包含名称空间的标签,如果没有名称空间,则为空
* @param qName 不包含名称空间的标签
* @param attributes 标签的属性集
* @throws SAXException
*/
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
super.startElement(uri, localName, qName, attributes);
if ("child".equals(qName)){
// 遍历属性
for (int i = 0; i < attributes.getLength(); i++) {
child = new Child();
if ("id".equals(attributes.getLocalName(i))){
child.setId(attributes.getValue(i));
}
}
}
currentTag = qName;
}
/**
* 解析标签的内容的时候调用
*
* @param ch 当前读取到的TextNode(文本节点)的字节数组
* @param start 字节开始的位置,为0表示读取全部
* @param length 当前TextNode的长度
* @throws SAXException
*/
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
super.characters(ch, start, length);
// 将当前读取TextNode转换为String
String value = new String(ch, start, length);
if ("age".equals(currentTag)){
child.setAge(Integer.parseInt(value));
}else if ("name".equals(currentTag)){
child.setName(value);
}
}
/**
* 标签结束时调用
*
* @param uri xml文档的命名空间
* @param localName 包含名称空间的标签,如果没有名称空间,则为空
* @param qName 不包含名称空间的标签
* @throws SAXException
*/
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
super.endElement(uri, localName, qName);
if ("child".equals(qName)){
list.add(child);
child = null;
}
// 当碰到结束标签时,将当前标签置为null
currentTag = null;
}
/**
* 文档解析结束时调用,该方法只会被调用一次
*
* @throws SAXException
*/
@Override
public void endDocument() throws SAXException {
super.endDocument();
endIndex = endIndex + 1;
System.out.println("xml文档解析结束,调用次数:" + endIndex);
}
}
- 加载配置文件,测试
public class SaxBuilderTest {
private static final InputStream in;
static {
in = SaxBuilderTest.class.getResourceAsStream("users.xml");
}
public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
// 实例化SAXParserFactory,由于SAXParserFactory的构造方法受保护的,因此无法new,必须的调用其提供的实例化方法newInstance
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
// 实例化SAXParser解析器
SAXParser parser = saxParserFactory.newSAXParser();
// 创建自定义Handler,该Handler继承DefaultHandler
ChildHandler handler = new ChildHandler();
// Handler顺序解析配置文件
parser.parse(in, handler);
List<Child> list = handler.getList();
for (Child child : list) {
System.out.println(child);
}
}
}
输出:
Child{id='1', age=18, name='jack'}
Child{id='2', age=20, name='michael'}
三、JDOM解析
JDOM是一个开源项目,它基于树形结构,利用纯Java的技术对XML文档实现解析、生成、序列化及多种操作。
JDOM与DOM非常类似,它是处理XML的纯JAVA API,API大量使用了Collections类,且JDOM仅使用具体类而不使用接口。JDOM 它自身不包含解析器。它通常使用 SAX2 解析器来解析和验证输入 XML 文档(尽管它还可以将以前构造的 DOM 表示作为输入)。
导入依赖:
<dependency>
<groupId>org.jdom</groupId>
<artifactId>jdom2</artifactId>
</dependency>
@SpringBootTest
public class JDOMTest {
@Test
public void jdomTest(){
try{
InputStream in = new FileInputStream(new File("src/test/resources/test.xml"));
List<Child> list = new ArrayList<>();
SAXBuilder saxBuilder = new SAXBuilder();
Document document = saxBuilder.build(in);
//获取xml文档的根节点
Element root = document.getRootElement();
// 获取根节点下的所有子节点
List<Element> childrens = root.getChildren();
// 遍历子节点
for (Element children : childrens) {
Child child = new Child();
child.setId(children.getAttributeValue("id"));
Element element = children.getChild("age");
Element element1 = children.getChild("height");
child.setAge(Integer.parseInt(element.getText()));
child.setHeight(Integer.parseInt(element1.getText()));
list.add(child);
}
System.out.println(list);
} catch (IOException | JDOMException e) {
e.printStackTrace();
}
}
}
四、DOM4j解析
dom4j是一款基于Java性能最好的xml解析工具,它使用接口和抽象类等方法来完成xml文件解析,因此,这个工具大家需要掌握。
操作步骤:
1. 创建Document对象
// 1. 读取XML文件,获得document对象
SAXReader reader = new SAXReader();
Document document = reader.read(in);
// 2. 解析xml报文
Document document = DocumentHelper.parseText("");
// 3. 主动创建document,主要用来生成XML文件的
Document document = DocumentHelper.createDocument();
2. 获取元素节点Element
1. 获取文档的根节点.
Element root = document.getRootElement();
2. 取得某个节点的子节点.
Element element=node.element("age");
3. 取得节点的内容
String text=node.getText();
4. 取得某节点下所有名为"age"的子节点,并进行遍历.
List nodes = rootElm.elements("age");
for (Iterator it = nodes.iterator(); it.hasNext();) {
Element elm = (Element) it.next();
}
5. 对某节点下的所有子节点进行遍历.
for(Iterator it=root.elementIterator();it.hasNext();){
Element element = (Element) it.next();
}
6. 在某节点下添加子节点
Element elm = newElm.addElement("朝代");
7. 设置节点文字. elm.setText("明朝");
8. 删除某节点.//childElement是待删除的节点,parentElement是其父节点 parentElement.remove(childElment);
9. 添加一个CDATA节点.Element contentElm = infoElm.addElement("content");contentElm.addCDATA(“cdata区域”);
3. 获取节点中的属性Attributes
1.取得某节点下的某属性
Element root=document.getRootElement();//属性名name
Attribute attribute=root.attribute("id");
2.取得属性的文字
String text=attribute.getText();
3.删除某属性
Attribute attribute=root.attribute("size"); root.remove(attribute);
4.遍历某节点的所有属性
Element root=document.getRootElement();
for(Iterator it=root.attributeIterator();it.hasNext();){
Attribute attribute = (Attribute) it.next();
String text=attribute.getText();
System.out.println(text);
}
5.设置某节点的属性和文字. newMemberElm.addAttribute("name", "sitinspring");
6.设置属性的文字 Attribute attribute=root.attribute("name"); attribute.setText("csdn");
测试:
XML报文:
<?xml version="1.0" encoding="UTF-8" ?>
<father id="张大">
<child id="张三">
<age>18</age>
<height>172</height>
</child>
<child id="张四">
<age>19</age>
<height>173</height>
</child>
</father>
public class Dom4jTest {
private static InputStream in;
static {
in = Dom4jTest.class.getResourceAsStream("/test.xml");
}
@Test
public void test() {
SAXReader reader = new SAXReader();
//解析xml报文
try {
Document document = reader.read(in);
Element rootElement = document.getRootElement();
Iterator it = rootElement.elementIterator();
while (it.hasNext()) {
Element element = (Element) it.next();
List<Attribute> attributes = element.attributes();
for (Attribute attr : attributes) {
System.out.println("节点名:" + attr.getName() + ",节点值:" + attr.getValue());
}
for(Iterator iterator = element.elementIterator(); iterator.hasNext();){
Element childElement = (Element)iterator.next();
if ("age".equals(childElement.getName())){
System.out.println("age = " + childElement.getText());
}
if ("height".equals(childElement.getName())){
System.out.println("height = " + childElement.getText());
}
}
}
} catch (DocumentException e) {
e.printStackTrace();
} finally {
if (in != null){
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
输出:
节点名:id,节点值:张三
age = 18
height = 172
节点名:id,节点值:张四
age = 19
height = 173
- 上一篇:XML文件的整体结构
- 下一篇:如何使用Java解析XML文档
相关推荐
- 图解面试题:SQL存储过程有什么用?
-
面试中有时候会问:存储过程有什么用?看了今天的知识,你就知道如何回答了。1.存储过程是什么?假如你每天要开车完成一些列重复的操作:车钥匙启动车,倒车。现在出现了一款新车,可以自动的完成这些重复的工作。...
- 零基础入门开始学习SQL Server存储过程
-
你好啊,我是晨希,今天我们来了解一下SQLServer的存储过程,通过这篇零基础的SQLServer存储过程入门指南文章。您将了解到什么是存储过程,如何创建、调用和优化它们,以及如何提高安全性和性...
- SQLSERVER:存储过程和函数
-
在SQLServer中,存储过程和函数是数据库编程的基础。它们允许开发者编写SQL脚本来执行复杂的操作,同时提供了代码重用和逻辑封装的能力。下面将通过一些实例来详细介绍存储过程和函数的使用。...
- 数据库基础知识:SQL Server存储过程入门必知
-
什么是存储过程SQL语句需要先编译然后执行,而存储过程(StoredProcedure)是一组为了完成特定功能的SQL语句集,经编译后存储在数据库中,用户通过指定存储过程的名字并给定参数(如果该存储...
- SQL储存过程
-
存储过程是一组预编译的SQL语句,可以保存在数据库中,并作为一个单元执行。它封装了复杂的操作逻辑,可以包含控制结构(如条件判断、循环)、输入参数、输出参数以及错误处理逻辑。存储过程的主要目的是提...
- sqlserver开窗及去重row_number() over(partition by c1 order by c2)
-
开窗函数/分析函数:over()开窗函数也叫分析函数,有两类:一类是聚合开窗函数,一类是排序开窗函数。...
- SQL server中exists用法
-
1、简介?不相关子查询:子查询的查询条件不依赖于父查询的称为不相关子查询?相关子查询:子查询的查询条件依赖于外层父查询的某个属性值的称为相关子查询。带Exists的子查询就是相关子查询...
- SQL 将两张包含相同字段和不同字段的表数据合并成一张表
-
第一种:两个表的相同字段数据查询后合并起来select工号,卡号,姓名,时间from(selectPeople_id工号,People_id卡号,People_name姓名,Rep...
- 「Oracle」 sql语句查询报错ORA-00904
-
Oracle报错ORA-00904:标识符无效一般情况下,标识符错误是因为:语句中的列名在表中不存在,修改sql语句或者修改列名即可。...
- SQL轻松入门(5):窗口函数
-
01前言标题中有2个字让我在初次接触窗口函数时,真真切切明白了何谓”高级”?说来也是一番辛酸史!话说,我见识了窗口函数的强大后,便磨拳擦掌的要试验一番,结果在查询中输入语句,返回的结果却是报错,Wh...
- SQLServer 日期函数大全
-
一、统计语句1、--统计当前【>当天00点以后的数据】SELECT*FROM表WHERECONVERT(Nvarchar,dateandtime,111)=CONVERT(Nv...
- 一文讲懂SQL联合查询UNION
-
大家好,我是宁一。今天讲解SQL教程第13课:UNION联合查询。...
- SQL中的INSERT INTO SELECT语句:数据复制的高效利器
-
SQL是数据库操作的重要语言,INSERTINTOSELECT语句则是其中的一把利器。本文将详细介绍这一语句的用法和优势,帮助读者更好地理解和运用。一、引言...
- SQL server查询-日期操作
-
常用的sql语句查询:...
- sql查询更新update select
-
针对一个上线的项目进行数据库优化,以便后期统计,遇到一个数据填充的问题,在此记录一下,各位如果也有这种问题,欢迎一起交流。表结构:...
- 一周热门
- 最近发表
- 标签列表
-
- grid 设置 (58)
- 移位运算 (48)
- not specified (45)
- patch补丁 (31)
- 导航栏 (58)
- context xml (46)
- scroll (43)
- dedecms模版 (53)
- c 视频教程下载 (33)
- listview排序 (33)
- firebug 使用 (31)
- characterencodingfilter (33)
- getmonth (34)
- hibernate教程 (31)
- label换行 (33)
- curlpost (31)
- android studio 3 0 (34)
- android应用开发 (31)
- html转js (35)
- 索引的作用 (33)
- checkedlistbox (34)
- localhost 8080 (32)
- 多态 (32)
- xmlhttp (35)
- mysql更改密码 (34)