一、XML常见解析方式
常见解析XML的方法主要有DOM和SAX
1.DOM解析方式-基于文档树
DOM,即文档对象模型(Document Object Model),将XML文档解析成树状模型并将其放入内存来完成解析工作的,而后对文档的操作都是在这个树状模型上完成的。这个在内存中的文档树将是文档实际大小的几倍。
2.SAX解析方式-事件驱动
即XML简单应用程序接口-Simple API for XML,通读整个文档,根据文档内容产生事件,而把对这些事件的处理交由事件处理器处理。
3.DOM与SAX解析方式的对比
SAX | DOM |
顺序读入文档并产生相应事件,可以处理任何大小的XML文档 | 在内存中创建文档树,不适于处理大型XML文档。 |
只能对文档按顺序解析一遍,不支持对文档的随意访问。 | 可以随意访问文档树的任何部分,没有次数限制。 |
只能读取XML文档内容,而不能修改 | 可以随意修改文档树,从而修改XML文档。 |
开发上比较复杂,需要自己来实现事件处理器。 | 易于理解,易于开发。 |
对开发人员而言更灵活,可以用SAX创建自己的XML对象模型。 | 已经在DOM基础之上创建好了文档树。 |
二、Java中解析XML
Sun公司提供了 java API for XML Parsing(JAXP)接口来使用SAX和DOM,通过JAXP,我们可以使用任何与JAXP兼容的XML解析器。
1.基础类与待解析XML
James 32 Kim 38 Joe 24
public class ClassInfo { private String no; private Liststudents; public String getNo() { return no; } public void setNo(String no) { this.no = no; } public List getStudents() { return students; } public void setStudents(List students) { this.students = students; } }
public class Person { private String no; private String name; private byte age; public String getNo() { return no; } public void setNo(String no) { this.no = no; } public String getName() { return name; } public void setName(String name) { this.name = name; } public byte getAge() { return age; } public void setAge(byte age) { this.age = age; } }
2.SAX方式解析
import java.util.ArrayList; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; import com.alibaba.fastjson.JSONObject; /** * SAX解析器 */ public class MemInfoParser extends DefaultHandler { /** * log4j日志 */ protected static Logger log = LogManager.getLogger(); private ClassInfo cls; private Person person; /** * */ private String preTag; /** * 文档开始调用 */ @Override public void startDocument() throws SAXException { cls = new ClassInfo(); cls.setStudents(new ArrayList<>()); } /** * 文档结束调用 */ @Override public void endDocument() throws SAXException { log.info("解析获得数据:" + JSONObject.toJSONString(cls)); } /** * 元素处理开始调用-多次 */ @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { switch (qName) { case "MemInfo": cls.setNo(attributes.getValue("class")); break; case "person": person = new Person(); person.setNo(attributes.getValue("no")); break; default: break; } preTag = qName; } /** * 元素处理结束调用-多次 */ @Override public void endElement(String uri, String localName, String qName) throws SAXException { switch (qName) { case "MemInfo": break; case "person": cls.getStudents().add(person); person = null; break; default: break; } preTag = null; } /** * 处理TextNode文本节点调用-多次 */ @Override public void characters(char[] ch, int start, int length) throws SAXException { //preTag为空,表示处理的是空白的文本节点,丢弃掉,PS:元素之间的空白部分SAX解析器会作为文本节点处理,如person、name节点间的空白 if (preTag == null) return; // 文本内容 String text = new String(ch, start, length); switch (preTag) { case "name": person.setName(text); break; case "age": person.setAge(Byte.parseByte(text)); break; default: break; } } }
测试类:
import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.xml.sax.XMLReader; public class SaxParserTest { public static void main(String[] args) throws Exception { String path = "/data/workspace/tec-demo/src/main/java/cn/tinyf/demo/xml/sax/MemInfo.xml"; // 创建解析工厂 SAXParserFactory factory = SAXParserFactory.newInstance(); // 创建解析器 SAXParser parser = factory.newSAXParser(); // 得到读取器 XMLReader reader = parser.getXMLReader(); // 设置内容处理器 MemInfoParser handler = new MemInfoParser(); reader.setContentHandler(handler); // 读取xml文档 reader.parse(path); } }
3.DOM方式读写
import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import com.alibaba.fastjson.JSONObject; /** * XML解析-Dom实现 */ public class MemInfoParser { public static void main(String[] args) { String path = "/data/workspace/tec-demo/src/main/java/cn/tinyf/demo/xml/MemInfo.xml"; System.out.println(JSONObject.toJSONString(parser(path))); } public static ClassInfo parser(String docPath) { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); // 从 DocumentBuilderFactory获取 DocumentBuilder实例 DocumentBuilder db; try { // 从 XML 文档获取 DOM 文档实例 db = dbf.newDocumentBuilder(); Document doc = db.parse(new File(docPath)); /* * 创建相关对象存储XML数据 */ ClassInfo cls = new ClassInfo(); ListstuList = new ArrayList<>(); cls.setStudents(stuList); // 获取文档节点中的班级信息 cls.setNo(doc.getDocumentElement().getAttribute("class")); /* * 获取所有学生节点,并遍历获取数据 */ NodeList stuNodes = doc.getElementsByTagName("person"); int len = stuNodes.getLength(); for (int i = 0; i < len; i++) { Element stu = (Element) stuNodes.item(i); Node eltName = stu.getElementsByTagName("name").item(0); Node eltAge = stu.getElementsByTagName("age").item(0); Person person = new Person(); person.setName(eltName.getFirstChild().getNodeValue()); person.setNo(stu.getAttribute("no")); person.setAge(Byte.parseByte(eltAge.getFirstChild().getNodeValue())); stuList.add(person); } return cls; } catch (ParserConfigurationException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; } }
/** * XML生成-dom方式 */ public class MemInfoBuilder { /** * log4j2日志 */ protected static Logger log = LogManager.getLogger(); public static void main(String[] args) { String xmlPath = "/data/workspace/tec-demo/src/main/java/cn/tinyf/demo/xml/dom/dom-data.xml"; // DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); // 从 DocumentBuilderFactory获取 DocumentBuilder实例 DocumentBuilder db; try { // 从 XML 文档获取 DOM 文档实例 db = dbf.newDocumentBuilder(); Document doc = db.newDocument(); /* * 生成文档树 */ // 根节点 Element root = doc.createElement("MemInfo"); // 设置根节点属性 root.setAttribute("class", "0501"); // 为根节点添加子节点数据 root.appendChild(createStuElement(doc, "1", "James", 32)); root.appendChild(createStuElement(doc, "2", "Kim", 38)); root.appendChild(createStuElement(doc, "3", "Joe", 24)); // 根节点添加到文档树 doc.appendChild(root); /* * 准备生成文件 */ // 设置XML声明中standalone为yes,即没有dtd和schema作为该XML的说明文档,且不显示该属性 doc.setXmlStandalone(true); // 创建TransformerFactory对象 TransformerFactory tff = TransformerFactory.newInstance(); // 创建Transformer对象 Transformer tf = tff.newTransformer(); // tf.setOutputProperty(OutputKeys.INDENT, "yes"); // 输出到文件 tf.transform(new DOMSource(doc), new StreamResult(new FileOutputStream(xmlPath))); } catch (ParserConfigurationException | FileNotFoundException | TransformerException e) { log.error(e); } } private static Element createStuElement(Document doc, String no, String name, int age) { Element stuElem = doc.createElement("person"); stuElem.setAttribute("no", no); //创建姓名节点 Element nameElem = doc.createElement("name"); nameElem.appendChild(doc.createTextNode(name)); //创建年龄节点 Element ageElem = doc.createElement("age"); ageElem.appendChild(doc.createTextNode(age + "")); //将姓名、年龄节点添加到学生节点中并返回 stuElem.appendChild(nameElem); stuElem.appendChild(ageElem); return stuElem; } }
三、其他解析器
1. JDOM
JDOM是一个开源项目,它基于树型结构,利用纯JAVA的技术对XML文档实现解析、生成、序列化以及多种操作。
Jdom可以和已有的XML技术如Simple API for XML (SAX)和 Document Object Model (DOM)相互协作。
实例>>
2.dom4j
dom4j是一个开源的Java的XML API,是jdom的升级品,用来读写XML文件的。dom4j是一个十分优秀的JavaXML API,具有性能优异、功能强大和极其易使用的特点,它的性能超过sun公司官方的dom技术。