通用SAX解析XML为Java对象

1 篇文章 0 订阅

前言

​ 有时候经常需要解析XML文件中的数据,将数据转化成Java中的对象,为了不重复编写代码,能否编写一个简单通用的XML解析类?即对于一般的XML文件,均可以解析成对应的Java对象?

SAX解析

​ SAX (Simple API for XML):该规范和DOM规范有着明显的区别,虽然SAX也是一套对XML文档进行分析的规范,但是其并不是基于XML文档的树形结构的,而是以数据流的方式,来先后处理所碰到的XML文档中的内容,由此我们可以推断出,SAX解析器肯定也不会象DOM解析器那样要把XML分析成树形数据结构后完全保存于内存中。事实的确如此,SAX解析器是顺序读取XML文档的内容,每碰到一个需要处理的内容就会出发一种类似于windows操作系统中消息系统的机制,也就是一种基于事件的处理机制,是一种回调的形式。SAX规范要求所有基于SAX规范的解析器,都有一套默认的事件处理函数,而用户都可以根据自己的需求来重写这些默认处理函数,从而按自己的要求处理当某事件发生时的工作。

​ 综上所述,可以了解到SAX解析器不需要将XML文档全部读入内存后,才开始分析,SAX解析器工作时所消耗的内存比DOM解析器要少;由于SAX的特点,它不能全局的获取XML的结构,所以它不能够随意读取XML文档中的任意一个节点内容;同时也因为SAX是顺序的读取XML文档,然后根据所碰到的东西来出发特定的处理,这也决定了SAX不能对不存在的XML文档进行处理,也就是说SAX不能创建新的XML文档;同理,SAX不能通过找到特定的某个节点,来对其进行修改。

Java反射机制

​ Java反射说的是在运行状态中,对于任何一个类,我们都能够知道这个类有哪些方法和属性。对于任何一个对象,我们都能够对它的方法和属性进行调用。我们把这种动态获取对象信息和调用对象方法的功能称之为反射机制。

​ 在这里,我们只需要用到反射机制中获取类属性的功能。例子如下:

User.java

package org.mao.reflect;

public class User {
	private String name;
	private String gender;
	private String birthday;
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getGender() {
		return gender;
	}
	public void setGender(String gender) {
		this.gender = gender;
	}
	public String getBirthday() {
		return birthday;
	}
	public void setBirthday(String birthday) {
		this.birthday = birthday;
	}
}

Main.java

public class Main {

	public static void main(String[] args) throws Exception{
		// 通过User.class获取Class对象
		Class clazz = Class.forName(User.class);
		System.out.println(clazz);
		// 创建类的对象
		Object obj = clazz.newInstance();
		// 获得字段
		Field[] declaredFields = clazz.getDeclaredFields();
		for (int i = 0; i < declaredFields.length; i++) {
			System.out.println(declaredFields[i].getName());
		}
		// 获得单个字段
		Field field = clazz.getDeclaredField("name");
		// 字段有可能是私有的,这时候要设置为可访问
		field.setAccessible(true);
		// 给属性赋值 即: name = 张三
		field.set(obj, "张三");
		// 获得属性值,相当于  String name = user.name;
		String name = (String) field.get(obj);
		field.setAccessible(false);
		System.out.println(name);
	}
}

在这里插入图片描述

编写XML文件对应的JavaBean

假设有如下XML文件:Book.xml

<?xml version="1.0" encoding="gb2312"?>
	<books>
		<book id="p0001" bookcategory="文艺" amount="150" remain="80" discount="8.5">
		    <title>三国演义</title>
		    <author>罗贯中</author>
		    <publisher>文艺出版社</publisher>
			<ISBN>0-764-58007-8</ISBN>
	        <price>80</price>
		</book>
		<book id="p0002" bookcategory="文艺" amount="300" remain="180" discount="8.7">
		    <title>红楼梦</title>
		    <author>曹雪芹</author>
		    <publisher>三秦出版社</publisher>
			<ISBN>7805468397</ISBN>
	        <price>22</price>
		</book>
		<book id="p0003" bookcategory="文艺" amount="200" remain="175" discount="8.5">
		    <title>西游记(上下册)</title>
		    <author>吴承恩</author>
		    <publisher>人民文学出版社</publisher>
			<ISBN>7020008739</ISBN>
	        <price>40.12</price>
		</book>
		<book id="p0189" bookcategory="计算机" amount="230" remain="160" discount="8.0">
			<title>VB.NET程序设计语言</title>
			<author>微软公司著</author>
			<publisher>高等教育出版社</publisher>
			<ISBN>7-04-013188-9</ISBN>
			<price>86.00</price>
		</book>
		<book id="p0028" bookcategory="计算机" amount="100" remain="90" discount="7.5">
			<title>JavaScript速成教程</title>
			<author>Michael Moncur著</author>
			<publisher>机械工业出版社</publisher>
			<ISBN>7-111-09070-5</ISBN>
			<price>28.00</price>
		</book>
	</books>

编写成对应的JavaBean:Book.java

public class Book {
	private String id;
	private String bookcategory;
	private String amount;
	private String remain;
	private String discount;
	private String title;
	private String author;
	private String publisher;
	private String ISBN;
	private String price;
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getBookcategory() {
		return bookcategory;
	}
	public void setBookcategory(String bookcategory) {
		this.bookcategory = bookcategory;
	}
	public String getAmount() {
		return amount;
	}
	public void setAmount(String amount) {
		this.amount = amount;
	}
	public String getRemain() {
		return remain;
	}
	public void setRemain(String remain) {
		this.remain = remain;
	}
	public String getDiscount() {
		return discount;
	}
	public void setDiscount(String discount) {
		this.discount = discount;
	}
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	public String getAuthor() {
		return author;
	}
	public void setAuthor(String author) {
		this.author = author;
	}
	public String getPublisher() {
		return publisher;
	}
	public void setPublisher(String publisher) {
		this.publisher = publisher;
	}
	public String getISBN() {
		return ISBN;
	}
	public void setISBN(String iSBN) {
		ISBN = iSBN;
	}
	public String getPrice() {
		return price;
	}
	public void setPrice(String price) {
		this.price = price;
	}
	@Override
	public String toString() {
		return "Book [id=" + id + ", bookcategory=" + bookcategory + ", amount=" + amount + ", remain=" + remain
				+ ", discount=" + discount + ", title=" + title + ", author=" + author + ", publisher=" + publisher
				+ ", ISBN=" + ISBN + ", price=" + price + "]";
	}
}

实现SAX解析

​ 新建一个类XmlParseHandler.java,该类需要继承DefaultHandler或者实现ContentHandler接口,这里我们通过继承DefaultHandler(实现了ContentHandler接口)的方式,该类是SAX解析的核心所在,要重写以下几个我们关心的方法。

  • startDocument():文档解析开始时调用,该方法只会调用一次
  • startElement(String uri, String localName, String qName,
    Attributes attributes):标签(节点)解析开始时调用
    1. uri:xml 文档的命名空间
    2. localName:标签的名字
    3. qName:带命名空间的标签的名字
    4. attributes:标签的属性集
  • characters(char[] ch, int start, int length):解析标签的内容的时候调用
    1. ch:当前读取到的TextNode(文本节点)的字节数组
    2. start:字节开始的位置,为0则读取全部
    3. length:当前TextNode的长度
  • endElement(String uri, String localName, String qName):标签(节点)解析结束后调用
  • endDocument():文档解析结束后调用,该方法只会调用一次

重写startElement

​ 在startElement中先判断当前的标签是否是对应的java对象,如果是则说明接下来是一个类的信息,新建一个Ocject对象用来存放这个对象的信息,封装到Object对象中。并记录当前的标签

public void startElement(String uri, String localName, String qName, Attributes attributes) {
		currentTag = qName;
    	// 是否的Java对象,是则创建对象来保存解析的数据
		if (nodeName.equals(qName)) {
			try {
				this.object = clazz.newInstance();
			} catch (Exception e) {
				System.out.println("对像创建失败");
			}
		}
		if (attributes != null) {
			Field[] declaredFields = clazz.getDeclaredFields();
			try {
				for (int i = 0; i < attributes.getLength(); i++) {
					for (int j = 0; j < declaredFields.length; j++) {
						String fieldname = declaredFields[j].getName();
						if (attributes.getQName(i).equals(fieldname)) {
							declaredFields[j].setAccessible(true);
							declaredFields[j].set(object, attributes.getValue(i));
							break;
						}
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

重写characters

	// 当解析到标签中的内容的时候调用(换行也是文本内容)
	public void characters(char[] ch, int start, int length) {
		String contents = new String(ch, start, length).trim();
		if (!contents.isEmpty()) {
			Field[] declaredFields = clazz.getDeclaredFields();
			try {
				for (int i = 0; i < declaredFields.length; i++) {
					String fieldname = declaredFields[i].getName();
					if (currentTag.equals(fieldname)) {
						declaredFields[i].setAccessible(true);
						declaredFields[i].set(object, contents);
						break;
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

重写endElement

​ 在这个方法中判断当前java对象是否解析结束,是则将对象添加到list中。

	@Override
	public void endElement(String uri, String localName, String name) throws SAXException {
        super.endElement(uri, localName, name);
        
        if (name.equals(nodeName)) {
        	list.add(object);
        	object = null;
        }
	}

完整代码

XmlParseHandler.java

public class XmlParseHandler extends DefaultHandler {
	// xml节点名称
	private String nodeName;
	// JavaBean类对象
	private Class clazz;
	// 存储解析的数据
	private Object object;
	// 存储所有解析的数据
	private List<Object> list;
	// 当前xml标签
	private String currentTag;
	// 构造方法 做些初始化工作
	public XmlParseHandler(String nodeName, Class clazz) {
		this.nodeName = nodeName;
		this.clazz = clazz;
		this.list = new ArrayList<Object>();
	}
	// 返回所有数据
	public List<Object> getList() {
		return list;
	}

	@Override
	public void startDocument() {
		System.out.println("开始解析文档");
	}

	@Override
	public void endDocument() {
		System.out.println("结束解析文档");
	}

	public void startElement(String uri, String localName, String qName, Attributes attributes) {
		currentTag = qName;
		if (nodeName.equals(qName)) {
			try {
				this.object = clazz.newInstance();
			} catch (Exception e) {
				System.out.println("对像创建失败");
			}
		}
		if (attributes != null) {
			Field[] declaredFields = clazz.getDeclaredFields();
			try {
				for (int i = 0; i < attributes.getLength(); i++) {
					for (int j = 0; j < declaredFields.length; j++) {
						String fieldname = declaredFields[j].getName();
						if (attributes.getQName(i).equals(fieldname)) {
							declaredFields[j].setAccessible(true);
							declaredFields[j].set(object, attributes.getValue(i));
							break;
						}
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	// 每一个标签结束时调用
	public void endElement(String uri, String localName, String qName) {
		if (nodeName.equals(qName)) {
			list.add(object);
			object = null;
		}
		currentTag = null;
	}

	// 当解析到标签中的内容的时候调用(换行也是文本内容)
	public void characters(char[] ch, int start, int length) {
		String contents = new String(ch, start, length).trim();
		if (!contents.isEmpty()) {
			Field[] declaredFields = clazz.getDeclaredFields();
			try {
				for (int i = 0; i < declaredFields.length; i++) {
					String fieldname = declaredFields[i].getName();
					if (currentTag.equals(fieldname)) {
						declaredFields[i].setAccessible(true);
						declaredFields[i].set(object, contents);
						break;
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}

MainApp.java

public class MainApp {
	public static void main(String[] args) throws Exception {
		try {
			// 创建指向的文件的对象
			File file = new File("src/Book.xml");
			// 1. 得到SAX解析工厂
			SAXParserFactory spf = SAXParserFactory.newInstance();
			// 2. 让工厂生产一个sax解析器
			SAXParser parser = spf.newSAXParser();
			// 要解析的node节点 和 解析为的object的类全路径
			XmlParseHandler handler = new XmlParseHandler("book", Book.class);
			// 3. 传入输入流和handler,解析
			parser.parse(new FileInputStream(file), handler);
			// 4. 获得解析结果
			List<Object> list = handler.getList();
			// 5. 遍历输出结果
			System.out.println("对其进行遍历,结果如下");
			for (Object object : list) {
				Book book = (Book) object;
				System.out.println(book.toString());
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

测试结果

在这里插入图片描述

解析其XML文件

如果更换要解析的XML文件,我们需要做如下步骤:

  1. 编写XML文件对应的实体Java类。

  2. 修改MainApp.java类。

    在这里插入图片描述

假设有如下XML文件:

<?xml version="1.0" encoding="UTF-8"?>
<users>
    <user id="1">
        <name>毕向东</name>
        <password>bxd123</password>
    </user>
    <user id="2">
        <name>韩顺平</name>
        <password>hsp123</password>
    </user>
    <user id="3">
        <name>马士兵</name>
        <password>msb123</password>
    </user>
</users>

编写JavaBean

public class User {
	private String id;
	private String name;
	private String password;

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	@Override
	public String toString() {
		return "User [id=" + id + ", name=" + name + ", password=" + password + "]";
	}

}

修改 MainApp.java

public class MainApp {
	public static void main(String[] args) throws Exception {
		try {
			// 创建指向的文件的对象
			File file = new File("src/User.xml");
			// 1. 得到SAX解析工厂
			SAXParserFactory spf = SAXParserFactory.newInstance();
			// 2. 让工厂生产一个sax解析器
			SAXParser parser = spf.newSAXParser();
			// 要解析的node节点 和 解析为的object的类全路径
			XmlParseHandler handler = new XmlParseHandler("user", User.class);
			// 3. 传入输入流和handler,解析
			parser.parse(new FileInputStream(file), handler);
			// 4. 获得解析结果
			List<Object> list = handler.getList();
			// 5. 遍历输出结果
			System.out.println("对其进行遍历,结果如下");
			for (Object object : list) {
				User book = (User) object;
				System.out.println(book.toString());
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

运行结果:
在这里插入图片描述

结语

效果还不错,但还是有许多不足。突发性的想法,谨此记录。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
要在Java中使用SAX解析XML,你可以按照以下步骤进行操作: 1. 导入相关的类和包,如`javax.xml.parsers.SAXParser`和`javax.xml.parsers.SAXParserFactory`。 2. 创建`SAXParserFactory`的实例。 3. 通过调用`SAXParserFactory`的`newSAXParser()`方法创建一个解析器。 4. 获取需要解析XML文档,并创建一个`File`对象来表示该文档。 5. 创建一个自定义的`SAXHandler`类,该类继承自`DefaultHandler`类,并重写需要的回调方法来处理XML元素和数据。 6. 调用解析器的`parse()`方法,传入文件和自定义的`SAXHandler`对象作为参数,开始解析XML文档。 你可以参考以下示例代码: ```java import java.io.File; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; public class TestDemo { public static void main(String[] args) throws Exception { // 1.实例化SAXParserFactory对象 SAXParserFactory factory = SAXParserFactory.newInstance(); // 2.创建解析SAXParser parser = factory.newSAXParser(); // 3.获取需要解析的文档,生成解析器,最后解析文档 File f = new File("books.xml"); SaxHandler dh = new SaxHandler(); parser.parse(f, dh); } } ``` 请注意,上述代码中的`SaxHandler`是一个自定义的类,你需要根据自己的需求来实现该类,以便在解析XML时处理相应的元素和数据。 XML文档如下所示: ```xml <?xml version="1.0" encoding="UTF-8"?> <books> <book id="001"> <title>Harry Potter</title> <author>J K. Rowling</author> </book> <book id="002"> <title>Learning XML</title> <author>Erik T. Ray</author> </book> </books> ``` 希望以上信息能够帮到你。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [java中使用sax解析xml的解决方法](https://download.csdn.net/download/weixin_38747216/12815749)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [在java中使用sax解析xml](https://blog.csdn.net/weixin_33884611/article/details/86303531)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值