XML是一种通用的数据交换格式,标记语言
树结构
XML在不同语言里面的解析方式都是一样的,只不过实现的语法不同而已。
SAX是基于事件流的解析
DOM是基于XML文档树结构的解析
SAX事件驱动
当解析器发现元素开始、元素结束、文本、文档的开始或者结束等时,发送事件,程序员编写响应这些事件的代码,保存数据。
有点类似于Servlet,它取读Xml会有一个顺序,就是取读元素的生命周期
优点:
不用事先调入整个文档,占用资源少。SAX解析器代码比DOM解析器代码小,适用于Applet,下载。
缺点:
不是持久的;事件过后,如果没有保存数据,那么数据就丢失了;无状态性;从事件中只能得到文本,但不知道文本属于哪个元素;使用场合:Applet;只需要XML文档的少量内容;机器内存少
DOM4J生成和解析XML文档:
我的理解:DOM4j首先它是DOM,所以是基于XML文档结构的解析。我的感觉dom4j相对于普通的dom来说应该是基于普通dom的一个自定义对象,类似于jquery相对于js
dom解析xml例子:
package com.test; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.TransformerFactoryConfigurationError; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.dom.Text; import org.xml.sax.SAXException; public class DomParseXml implements XmlDocument{ /** * DOM解析和生成Xml文档 * 分析: * 解析器读入整个文档,然后构建一个驻留内存的树结构,然后代码使用DOM接口来操作这个树结构 * * 优点: * 整个文档树在内存中,编译操作;支持删除、修改、重新排序等多种功能 * 缺点: * 将整个文档存入内存内存,浪费时间和空间 * 使用场合:一旦解析了文档还需要多次访问这些数据;硬件资源充足 */ /** 一些基础: DocumentBuilderFactory 定义工厂 API,使应用程序能够从 XML 文档获取生成 DOM 对象树的解析器。 两个静态方法: abstract DocumentBuilder newDocumentBuilder() 使用当前配置的参数创建一个新的 DocumentBuilder 实例。 static DocumentBuilderFactory newInstance() 获取 DocumentBuilderFactory 的新实例。 DocumentBuilder 定义 API, 使其从 XML 文档获取 DOM 文档实例。使用此类,应用程序员可以从 XML 获取一个 Document。 获取此类的实例之后,将可以从各种输入源解析 XML。这些输入源有 InputStreams、Files、URL 和 SAX InputSources。 方法: abstract Document newDocument() 获取 DOM Document 对象的一个新实例来生成一个 DOM 树。 Document parse(File f) abstract Document parse(InputSource is) Document parse(InputStream is) Document parse(InputStream is, String systemId) Document parse(String uri) void reset() Node: 该 Node 接口是整个文档对象模型的主要数据类型。它表示该文档树中的单个节点。当实现 Node 接口的所有对象公开处理子节点的方法时,不是实现 Node 接口的所有对象都有子节点。例如,Text 节点可能没有子节点,且将子节点添加到这样的节点将导致引发 DOMException。 属性: static short ATTRIBUTE_NODE 该节点为 Attr。 static short DOCUMENT_NODE 该节点为 Document。 static short ELEMENT_NODE 该节点为 Element。 static short TEXT_NODE 该节点为 Text 节点。 方法: Node appendChild(Node newChild) 将节点 newChild 添加到此节点的子节点列表的末尾。 Node cloneNode(boolean deep) 返回此节点的副本,即允当节点的一般复制构造方法。 NamedNodeMap getAttributes() 包含此节点的属性的 NamedNodeMap(如果它是 Element);否则为 null NodeList getChildNodes() 包含此节点的所有子节点的 NodeList。 Node getFirstChild() 此节点的第一个子节点。 Node getLastChild() 此节点的最后一个节点。 Node getNextSibling() 直接在此节点之后的节点。 String getNodeName() 此节点的名称,取决于其类型;参见上表。 short getNodeType() 表示基础对象的类型的节点,如上所述。 String getNodeValue() 此节点的值,取决于其类型;参见上表。 Document getOwnerDocument() 与此节点相关的 Document 对象。 Node getParentNode() 此节点的父节点。 Node getPreviousSibling() 直接在此节点之前的节点。 String getTextContent() 此属性返回此节点及其后代的文本内容 boolean hasAttributes() 返回此节点(如果它是一个元素)是否具有任何属性。 boolean hasChildNodes() 返回此节点是否具有任何子节点。 Node insertBefore(Node newChild, Node refChild) 在现有子节点 refChild 之前插入节点 newChild。 Node removeChild(Node oldChild) 从子节点列表中移除 oldChild 所指示的子节点,并将其返回。 Node replaceChild(Node newChild, Node oldChild) 将子节点列表中的子节点 oldChild 替换为 newChild,并返回 oldChild 节点。 void setNodeValue(String nodeValue) 此节点的值,取决于其类型;参见上表 void setTextContent(String textContent) 此属性返回此节点及其后代的文本内容。 Document继承Node Document 接口表示整个 HTML 或 XML 文档。从概念上讲,它是文档树的根,并提供对文档数据的基本访问。 方法: DocumentType getDoctype() 与此文档相关的文档类型声明(参见 DocumentType)。 String getXmlVersion() 作为 XML 声明 的一部分指定此文档版本号的属性。 String getXmlEncoding() 作为 XML 声明的一部分,指定此文档编码的属性。 Attr createAttribute(String name) 创建给定名称的attribute (属性节点) Element createElement(String tagName) 创建指定类型的元素。(元素节点) Text createTextNode(String data) 创建给定指定字符串的 Text 节点。 (文本节点) Element getDocumentElement() 获取Xml根节点 Element getElementById(String elementId) 根据id得到节点 NodeList getElementsByTagName(String tagname) 按文档顺序返回包含在文档中且具有给定标记名称的所有 Element 的 NodeList。 Attr继承Node String getName() 返回此属性的名称。 Element getOwnerElement() 此属性连接到的 Element 节点;如果未使用此属性,则为 null。 String getValue() 检索时,该属性值以字符串形式返回。 boolean isId() 返回是否此属性属于类型 ID(即包含其所有者元素的标识符)。 void setValue(String value) 检索时,该属性值以字符串形式返回。 Element继承Node String getAttribute(String name) 通过名称获得属性值。 Attr getAttributeNode(String name) NodeList getElementsByTagName(String name) String getTagName() 元素的名称。 boolean hasAttribute(String name) void removeAttribute(String name) Attr removeAttributeNode(Attr oldAttr) 移除指定的属性节点。 void setAttribute(String name, String value) 添加一个新属性。 Attr setAttributeNode(Attr newAttr) Text 该 Text 接口继承自 CharacterData,并且表示 Element 或 Attr 的文本内容(在 XML 中称为字符数据) String getWholeText() 返回 Text 节点(逻辑上与此节点相邻的节点)的以文档顺序串接的所有文本。 boolean isElementContentWhitespace() 返回此文本节点是否包含 元素内容空白符,即经常所称的“可忽略的空白符”。 Text replaceWholeText(String content) 将当前节点和所有逻辑上相邻的文本节点的文本替换为指定的文本。 Text splitText(int offset) 在指定的 offset 处将此节点拆分为两个节点,并将二者作为兄弟节点保持在树中。 */ /* TransformerFactory 生成transformer实例的工厂 方法: static TransformerFactory newInstance() 获取 TransformerFactory 的新实例。 abstract Transformer newTransformer() 创建执行从 Source 到 Result 的复制的新 Transformer。 Transformer 此抽象类的实例能够将源树转换为结果树。 使用此实例处理来自不同源的 XML,并将转换输出写入各种接收器。 方法: abstract void transform(Source xmlSource, Result outputTarget) 将 XML Source 转换为 Result。 接口Source:实现此接口的对象包含充当源输入(XML 源或转换指令)所需的信息 实现类: DomSource:以 Document Object Model(DOM)树的形式充当转换 Source 树的持有者。 构造: DOMSource() DOMSource(Node n) DOMSource(Node node, String systemID) 创建带有 DOM 节点的新输入源,系统 ID 也作为基本 URI 被传入。 方法: Node getNode() 获取表示 Source DOM 树的节点。 void setNode(Node node) 设置将表示 Source DOM 树的节点。 StreamSource:以 XML 标记流的形式充当转换 Source 的持有者。 注: 由于内部使用了 Reader 或 InputStream 实例的缘故,StreamSource 实例只能使用一次。 构造: StreamSource() StreamSource(File f) StreamSource(InputStream inputStream) StreamSource(Reader reader) StreamSource(String systemId) 从 URL 构造 StreamSource。 方法: InputStream getInputStream() Reader getReader() void setInputStream(InputStream inputStream) void setReader(Reader reader) 接口Result 实现此接口的对象包含构建转换结果树所需的信息。 DomResult:以 Document Object Model(DOM)树的形式充当转换结果树的持有者。 StreamResult:充当转换结果的持有者,可以为 XML、纯文本、HTML 或某些其他格式的标记。 方法: StreamResult() StreamResult(File f) StreamResult(OutputStream outputStream) StreamResult(String systemId) StreamResult(Writer writer) */ private Document document; private String fileName; StringBuilder sb=new StringBuilder(); public static void main(String[] args) { DomParseXml dpx=new DomParseXml(); // dpx.parseXml("src/demo.xml"); dpx.createXml("src/demo01.xml"); } @Override public void createXml(String fileName) { //生成XML文档 //我们前面在init方法中初始化了document,所以这里可以直接使用 init(); //现在有了一个Document对象 //1.我可以为他赋值version document.setXmlVersion("1.0"); //2现在的document只有一个版本信息,没什么作用,所以我要给它值 Element root=document.createElement("languages"); Element lan1=document.createElement("language"); lan1.setAttribute("id", "1"); Element name1=document.createElement("name"); name1.setTextContent("Java"); Element editor1=document.createElement("editor"); editor1.setTextContent("eclipes"); lan1.appendChild(name1); lan1.appendChild(editor1); Element lan2=document.createElement("language"); lan2.setAttribute("id", "2"); Element name2=document.createElement("name"); name2.setTextContent("C"); Element editor2=document.createElement("editor"); editor2.setTextContent("cfree"); lan2.appendChild(name2); lan2.appendChild(editor2); Element lan3=document.createElement("language"); lan3.setAttribute("id", "3"); Element name3=document.createElement("name"); name3.setTextContent("C#"); Element editor3=document.createElement("editor"); editor3.setTextContent("xcode"); lan3.appendChild(name3); lan3.appendChild(editor3); root.appendChild(lan1); root.appendChild(lan2); root.appendChild(lan3); document.appendChild(root); //3.如何将document内容赋值到输入流或者写入输出流呢?这样才能生成Xml本地文件使用transformer的transform方法 try { Transformer trans=TransformerFactory.newInstance().newTransformer(); trans.transform(new DOMSource(document), new StreamResult(new FileOutputStream(fileName))); } catch (TransformerConfigurationException e) { e.printStackTrace(); } catch (TransformerFactoryConfigurationError e) { e.printStackTrace(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (TransformerException e) { e.printStackTrace(); } } public void init(){ DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();//单例模式 try { DocumentBuilder builder=factory.newDocumentBuilder(); this.document=builder.newDocument();//初始化document } catch (ParserConfigurationException e) { e.printStackTrace(); } } @Override public void parseXml(String fileName) { //首先我们需要将Xml读取 File xmlcontent=new File(fileName); //解析文件得到document对象 try { document=DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(xmlcontent); /* * 我这里只是热热身,下面要真正解析完全我们的document Element e=document.getDocumentElement(); sb.append("<"+e.getNodeName()); NamedNodeMap nodeMap=e.getAttributes(); for(int i=0;i<nodeMap.getLength();i++){ Attr att=(Attr) nodeMap.item(i); sb.append(" "+att.getName()+"='"+att.getValue()+"'"); } sb.append(" > "); NodeList nodeList=e.getChildNodes(); //与node有关的这几个看起来像List却都不能使用forEach for(int i=0;i<nodeList.getLength();i++){ Node ele=nodeList.item(i); //我们发现空格也是一个文本节点,所以我们需要规避掉它 if(ele.getNodeType()==Node.TEXT_NODE){ continue; }else{ sb.append(" <"+ele.getNodeName()+"></"+ele.getNodeName()+"> "); } } sb.append("</"+e.getNodeName()+">"); System.out.println(sb.toString()); */ /** * 首先分析一下: * document包含element * element中包含attr,text,可能包含其他element * 我们知道,xml的默认规范,有意义的文本节点的一般是没有兄弟节点的,也就是value一般是包含在标签之内的 * 所以,如果element中还有element标签,它就应该忽视它的子元素文本节点 * element和document其实很类似的,它也可以拥有其子节点和文本节点,属性等等。所以我们要完全解析一个document,可以尝试使用方法的递归 */ parseNode(document); System.out.println(sb); } catch (SAXException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (ParserConfigurationException e) { e.printStackTrace(); } } public void parseNode(Node node){ short type=node.getNodeType(); if(type==Node.DOCUMENT_NODE){ Document d=(Document) node; sb.append("<?xml version=""+d.getXmlVersion()+"" encoding=""+d.getXmlEncoding()+""?>"); Element e=d.getDocumentElement(); parseNode(e); }else if(type==Node.ELEMENT_NODE){ Element e=(Element) node; sb.append("<"+e.getNodeName()); NamedNodeMap attList=e.getAttributes(); for(int i=0;i<attList.getLength();i++){ Attr att=(Attr) attList.item(i); sb.append(" "+att.getName()+"=""+att.getValue()+"""); } sb.append(">"); NodeList nodeList=e.getChildNodes(); for(int i=0;i<nodeList.getLength();i++){ Node node_1=nodeList.item(i); if(nodeList.getLength()==1 && node_1.getNodeType()==Node.TEXT_NODE){ Text t=(Text) node_1; sb.append(t.getTextContent()); }else{ if(node_1.getNodeType()==Node.TEXT_NODE){ continue; }else if(node_1.getNodeType()==Node.ELEMENT_NODE){ parseNode(node_1); } } } sb.append("</"+e.getNodeName()+">"); } } } package com.test; public interface XmlDocument { /** * 分析: * 首先定义一个操作XML文档的接口XmlDocument,它定义了Xml建立与解析的接口 */ public void createXml(String fileName);//生成Xml,参数是文件路径 public void parseXml(String fileName);//解析XML,参数是文件路径 }
SAX解析的例子:
package com.test; import java.io.File; import java.io.IOException; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.junit.Test; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; /* * SAX文档解析 */ /* 基础了解: SAXParserFactory 定义工厂 API,使应用程序能够配置和获取基于 SAX 的解析器以解析 XML 文档。 static SAXParserFactory newInstance() 获取 SAXParserFactory 的一个新实例。 abstract SAXParser newSAXParser() 使用当前配置的工厂参数创建 SAXParser 的一个新实例。 SAXParser:定义包装 XMLReader 实现类的 API。 获取此类的实例之后,将可以从各种输入源解析 XML。这些输入源为 InputStream、File、URL 和 SAX InputSource。 方法: abstract Parser getParser() 返回由此类的实现封装的 SAX 解析器。 XMLReader getXMLReader() 返回由此类的实现封装的 XMLReader。 void parse(File f, DefaultHandler dh) 使用指定的 DefaultHandler 将指定文件的内容解析为 XML。 void parse(File f, HandlerBase hb) 使用指定的 HandlerBase 将指定文件的内容解析为 XML。 void parse(InputSource is, DefaultHandler dh) void parse(InputSource is, HandlerBase hb) void parse(InputStream is, DefaultHandler dh) void parse(InputStream is, HandlerBase hb) DefaultHandler:事件处理默认基类 方法: void characters(char[] ch, int start, int length) 接收元素中字符数据的通知。 void endDocument() 接收文档结束的通知。 void endElement(String uri, String localName, String qName) 接收元素结束的通知。 void ignorableWhitespace(char[] ch, int start, int length) 接收元素内容中可忽略空白的通知。 void startDocument() 接收文档开始的通知。 void startElement(String uri, String localName, String qName, Attributes attributes) 接收元素开始的通知。 HandlerBase:已经过时 */ public class SaxParseXml implements XmlDocument{ @Override public void createXml(String fileName) { //SAX好像不常用来创建xml } @Override public void parseXml(String fileName) { //String fileName //String fileName="src/demo.xml"; SAXParserFactory saxfac=SAXParserFactory.newInstance(); try { StringBuilder sb=new StringBuilder(); SAXParser saxpa=saxfac.newSAXParser(); saxpa.parse(new File(fileName),new MyHanler(sb)); System.out.println(sb); } catch (ParserConfigurationException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } class MyHanler extends DefaultHandler{//当然我这里是将它处理成为字符串,而且是整个document中的所有element以及其内容,如果你需要部分内容的话,可以使用逻辑判断 private StringBuilder sb; //无返回值的话通常可以使用构造传递参数 public MyHanler(StringBuilder sb){ this.sb=sb; } public void characters(char[] ch, int start, int length){ sb.append(ch,start,length); } public void endDocument(){ //System.out.println("this document will close"); } public void endElement(String uri, String localName, String qName){ sb.append("</"+qName+">"); } public void startDocument(){ //System.out.println("this document begin"); } public void startElement(String uri, String localName, String qName, Attributes attributes){ //qName代表元素节点名 //System.out.println(qName+" will start"); sb.append("<"+qName); for(int i=0;i<attributes.getLength();i++){ sb.append(" "+attributes.getQName(i)+"=""+attributes.getValue(i)+"""); } sb.append(">"); } }
dom4j解析的例子(这个不怎么完美):
package com.test; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.Iterator; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.DocumentHelper; import org.dom4j.Element; import org.dom4j.io.SAXReader; import org.dom4j.io.XMLWriter; /** * * dom4j生成与解析XML文档 * */ public class Dom4jParseXml implements XmlDocument{ public static void main(String[] args) { Dom4jParseXml d=new Dom4jParseXml(); // d.createXml("src/nihao.xml"); d.parseXml("src/demo01.xml"); } @Override public void createXml(String fileName) { Document document=DocumentHelper.createDocument(); document.setXMLEncoding("utf-8"); Element e=document.addElement("books"); Element b1=e.addElement("book"); b1.addAttribute("id", "1").addAttribute("type", "mingzu"); b1.addElement("name").setText("红楼梦"); b1.addElement("author").setText("曹雪芹"); Element b2=e.addElement("book"); b2.addAttribute("id", "2").addAttribute("type", "mingzu"); b2.addElement("name").setText("西游记"); b2.addElement("author").setText("吴承恩"); Element b3=e.addElement("book"); b3.addAttribute("id", "3").addAttribute("type", "mingzu"); b3.addElement("name").setText("水浒传"); b3.addElement("author").setText("施耐庵"); Element b4=e.addElement("book"); b4.addAttribute("id", "4").addAttribute("type", "mingzu"); b4.addElement("name").setText("三国演义"); b4.addElement("author").setText("罗贯中"); //将这个document生成文档,方式有很多 // System.out.println(document.asXML()); //这里输出的是生成的xml格式的字符串,我们可以将它写入输出流不就可以,这里就不演示 //这里注意下编码解码的问题,要不然可能乱码 XMLWriter xw=null; try { FileWriter fw=new FileWriter(fileName); xw=new XMLWriter(fw); xw.write(document); } catch (IOException e1) { e1.printStackTrace(); }finally{ try { if(xw!=null){ xw.close(); } } catch (IOException e1) { e1.printStackTrace(); } } } @Override public void parseXml(String fileName) { File inputXml=new File(fileName); SAXReader saxReader=new SAXReader(); try { Document document=saxReader.read(inputXml); Element root=document.getRootElement(); for(Iterator i=root.elementIterator();i.hasNext();){ Element ele=(Element) i.next(); for(Iterator j=ele.elementIterator();j.hasNext();){ Element node=(Element) j.next(); System.out.println(node.getName()+":"+node.getText()); } } } catch (DocumentException e) { e.printStackTrace(); } } }