android开发中的DOM和SAX使用的工厂模式分析

本文介绍了XML文件解析的两种主流方式:DOM和SAX,并详细分析了工厂模式在XML解析中的应用,通过模拟代码展示了根据操作系统类型选择不同解析器的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

     第一节先简要介绍XML文件解析的两种方案,因为关于XML文件的解析应该是我们写java代码时都会遇到的。第二节是对工厂模式的介绍,工厂模式是设计模式的最常见的一种,这也是为了理解下面的代码分析部分做铺垫。第三节是根据java代码分析其中的工厂设计模式,并模拟其实现。

  本文的重点集中在对工厂模式的分析,之前的DOM和SAX的案例转自网络。

1 java实现XML解析的两种模式

SAX (Simple API for XML) 和 DOM (Document Object Model) 是当前两个主要的XML API,几乎所有商用的xml 解析器都同时实现了这两个接口。因此如果你的程序使用了SAX或者DOM APIs,那么你的程序对xml解析器是透明。

1. DOM以一个分层的对象模型来映射xml文档。而SAX将文档中的元素转化为对象来处理。

2. DOM将文档载入到内存中处理,而SAX则相反,它可以检测一个即将到来的 XML流,由此并不需要所有的XML代码同时载入到内存中

1.1 DOM

在使用DOM的情况下,解析器做了绝大多数事情读入XML文档在这基础之上创建对象模型,然后给你一个对这个对象的引用(一个 Document对象),因而你可以操作使用它。

package com.alisoft.facepay.framework.bean;

import java.io.FileInputStream;

import java.io.FileNotFoundException;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.InputStream;

import java.io.PrintWriter;

import javax.xml.parsers.DocumentBuilder;

import javax.xml.parsers.DocumentBuilderFactory;

import javax.xml.parsers.ParserConfigurationException;

import javax.xml.transform.OutputKeys;

import javax.xml.transform.Transformer;

import javax.xml.transform.TransformerConfigurationException;

import javax.xml.transform.TransformerException;

import javax.xml.transform.TransformerFactory;

import javax.xml.transform.dom.DOMSource;

import javax.xml.transform.stream.StreamResult;

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;

/** * * @author hongliang.dinghl * DOM生成与解析XML文档 */

public class DomDemo implements XmlDocument {

private Document document;

private String fileName;

public void init() {

try {

DocumentBuilderFactory factory = DocumentBuilderFactory

.newInstance();

DocumentBuilder builder = factory.newDocumentBuilder();

this.document = builder.newDocument();

catch (ParserConfigurationException e) {

System.out.println(e.getMessage());

}

}

public void createXml(String fileName) {

Element root = this.document.createElement("employees");

this.document.appendChild(root);

Element employee = this.document.createElement("employee");

Element name = this.document.createElement("name");

name.appendChild(this.document.createTextNode("dhl"));

employee.appendChild(name);

Element sex = this.document.createElement("sex");

sex.appendChild(this.document.createTextNode("m"));

employee.appendChild(sex);

Element age = this.document.createElement("age");

age.appendChild(this.document.createTextNode("30"));

employee.appendChild(age);

root.appendChild(employee);

TransformerFactory tf = TransformerFactory.newInstance();

try {

Transformer transformer = tf.newTransformer();

DOMSource source = new DOMSource(document);

transformer.setOutputProperty(OutputKeys.ENCODING"gb2312");

transformer.setOutputProperty(OutputKeys.INDENT"yes");

PrintWriter pw = new PrintWriter(new FileOutputStream(fileName));

StreamResult result = new StreamResult(pw);

transformer.transform(source, result);

System.out.println("生成XML文件成功!");

catch (TransformerConfigurationException e) {

System.out.println(e.getMessage());

catch (IllegalArgumentException e) {

System.out.println(e.getMessage());

catch (FileNotFoundException e) {

System.out.println(e.getMessage());

catch (TransformerException e) {

System.out.println(e.getMessage());

}

}

public void parserXml(String fileName) {

try {

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

DocumentBuilder db = dbf.newDocumentBuilder();

Document document = db.parse(fileName);

NodeList employees = document.getChildNodes();

for (int i = 0; i < employees.getLength(); i++) {

Node employee = employees.item(i);

NodeList employeeInfo = employee.getChildNodes();

for (int j = 0; j < employeeInfo.getLength(); j++) {

Node node = employeeInfo.item(j);

NodeList employeeMeta = node.getChildNodes();

for (int k = 0; k < employeeMeta.getLength(); k++) {

System.out.println(employeeMeta.item(k).getNodeName()

":" + employeeMeta.item(k).getTextContent());

}

}

}

System.out.println("解析完毕");

catch (FileNotFoundException e) {

System.out.println(e.getMessage());

catch (ParserConfigurationException e) {

System.out.println(e.getMessage());

catch (SAXException e) {

System.out.println(e.getMessage());

catch (IOException e) {

System.out.println(e.getMessage());

}

}

}

1.2 SAX

SAX没有期待解析器去做这么多工作,所有SAX 要求的是解析器应该读入XML文档,同时根据所遇到的XML文档的标签向一个事件处理程序发出一系列事件,比如元素开始和元素结束,而事件处理器则处理该信息:你要自己写一个XML文档处理器类(XML document handler class)来处理这些事件,这意味着使所有标签事件有意义还有用你自己的对象模型创建对象。SAX 解析器根本不创建任何对象,它只是将事件传递给您的应用程序。如果希望基于那些事件创建对象,这将由您来完成。 
SAX 处理是如何工作的 

SAX 在读取 XML 流的同时处理它们,这很像以前的自动收报机纸带(ticker tape)。
而分析这个代码片断的 SAX 处理器一般情况下将产生以下事件: 

Start document
Start element (samples)
Characters (white space)
Start element (server)
Characters (UNIX)
End element (server)
Characters (white space)
Start element (monitor)
Characters (color)
End element (monitor)
Characters (white space)
End element (samples)

SAX API 允许开发人员捕捉这些事件并对它们作出反应。

SAX 处理涉及以下步骤:
    创建一个事件处理程序。 
    创建 SAX 解析器。 
    向解析器分配事件处理程序。 
    解析文档,同时向事件处理程序发送每个事件。

package com.alisoft.facepay.framework.bean; 

import java.io.FileInputStream;   

import java.io.FileNotFoundException;  

import java.io.IOException;   

import java.io.InputStream;  

import javax.xml.parsers.ParserConfigurationException; 

import javax.xml.parsers.SAXParser;   

import javax.xml.parsers.SAXParserFactory;   

import org.xml.sax.Attributes;   

import org.xml.sax.SAXException;  

import org.xml.sax.helpers.DefaultHandler;  

/**  *   * @author hongliang.dinghl  * SAX文档解析  */ 

public class SaxDemo implements XmlDocument {  

public void createXml(String fileName) { 

  System.out.println("<<"+filename+">>");  

}  

public void parserXml(String fileName) { 

SAXParserFactory saxfac = SAXParserFactory.newInstance();   

try {  

SAXParser saxparser = saxfac.newSAXParser();  

InputStream is = new FileInputStream(fileName);  

saxparser.parse(is, new MySAXHandler());   

catch (ParserConfigurationException e) { 

  e.printStackTrace();  

catch (SAXException e) {  

e.printStackTrace();  

catch (FileNotFoundException e) {

e.printStackTrace();  

catch (IOException e) { 

e.printStackTrace();  

}   

class MySAXHandler extends DefaultHandler 

{   

boolean hasAttribute = false;  

Attributes attributes = null;  

public void startDocument() throws SAXException {  

System.out.println("文档开始打印了");   

}   

public void endDocument() throws SAXException { 

System.out.println("文档打印结束了");

}

public void startElement(String uri, String localName, 

String qName,   Attributes attributes)

throws SAXException

{   

if (qName.equals("employees")) {   

return;   

}   

if (qName.equals("employee")) {   

System.out.println(qName);   

}   

if (attributes.getLength() > 0) {   

this.attributes = attributes;   

this.hasAttribute = true;   

}   

}   

public void endElement(String uri, String localName, String qName)   

throws SAXException {   

if (hasAttribute && (attributes != null)) {   

for (int i = 0; i < attributes.getLength(); i++) {   

System.out.println(attributes.getQName(0)

+ attributes.getValue(0));   

}   

}   

}   

public void characters(char[] ch, int start, int length)  

throws SAXException {   

System.out.println(new String(ch, start, length));   

}   

}  

工厂模式

工厂模式是设计模式中最常见的设计模式之一,一般来说工厂模式分为简单工厂、工厂方法、抽象工厂三种模式。工厂方法模式和抽象工厂模式之间之间存在一种微妙的关系,我们可以将工厂方法模式看做抽象工厂模式的一个特例或者一个子集。

下面是对二者之间做一个比较:

factory method针对的是一个产品等级结构  
abstract factory是面向多个产品等级结构的

工厂方法模式:一个抽象产品类,可以派生出多个具体产品类。 
                              一个抽象工厂类,可以派生出多个具体工厂类。 
                              每个具体工厂类只能创建一个具体产品类的实例。 
抽象工厂模式:多个抽象产品类,每个抽象产品类可以派生出多个具体产品类。 
                              一个抽象工厂类,可以派生出多个具体工厂类。 
                              每个具体工厂类可以创建多个具体产品类的实例。 
  
区别:工厂方法模式只有一个抽象产品类,而抽象工厂模式有多个。
      工厂方法模式的具体工厂类只能创建一个具体产品类的实例,而抽象工厂模式可以创建多个。

 

工厂模式在解析XML文件时的运用

3.1DocumentBuilderFactory说起

从上面的使用DOM解析XML文件的例子中我们主要使用了几个类:DocumentBuilderFactoryDocumentBuilderDocumentElementNodeListNode

可奇怪的是这几个类是分布在不同系列的JAR包中。

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;

   DocumentElementNodeListNode这几个类是在org.w3c.dom.*系列包中,这里遵循的是w3cWorld Wide Web Consortium万维网联盟)组织制定的DOM规范。这一部分代码,理论上是与平台无关的。

   Document对象是java扩展包提供的对xml文件解析得到的,也就是javax.xml.parsers.*系列包。这里的DocumentBuilderFactoryDocumentBuilder就是构建最终Document对象的工厂。

  打个比方,就是我们制定了手机的规范,手机应该是个什么样子,能打电话、接电话、发送短信等等;但是我们并没制定手机生产厂家的规范。不管是什么样的厂家,只要你生产的手机符合规范,我们就称其为手机。这里的Document就是手机,DocumentBuilderFactory就是手机厂商,DocumentBuilder就是生产车间。

下面我对DocumentBuilderFactory这个类背后的情况进行分析。

之前DOM解析XML文件的例子代码中有这样的一段:

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

DocumentBuilder db = dbf.newDocumentBuilder();

Document document = db.parse(fileName);

这里为什么我们不直接new一个DocumentBuilderFactory,而是调用了一个静态方法去实例化一个对象呢?

为什么DocumentBuilder不直接构建自身,而是使用Factory实例化一个对象呢?

3.2 实例化-隐藏了真实的对象类型

我们可以尝试new一个DocumentBuilderFactory对象,结果发现这个类其实是个抽象类,根本就不能直接实例化。那么,现在我们发现,其实它调用的静态方法其实并不是实例化自身,而是实例化了一个子类对象。(PS:典型静态方法实例化自身的情况是单例模式)。

我曾有个很傻的想法,认为子类的实例化可以放置在父类的构造函数中,当然前提是父类不是抽象类。如下:

Class FatherClass{

FatherClass(){

    int  param = getEnvironmentParam();

    Switch(param){

    {

      case 1:

   this = new ChileClass1();

   break;

case 2:

   this = new ChildClass2();

   break;

}

}

}

Class ChildClass1 extends FatherClass{

ChileClass1(){

     super()

}

     }

Class ChildClass2 extends FatherClass{

ChileClass2(){

     super()

}

     }

这样我们就可以不必要用一个静态方法实例化子对象了,new出来的就是子类对象,可事实是这样是行不通的。仔细看看上面的代码,发现其实这是个死循环。本来子类构造的时候是要调用父类的构造函数,现在父类的构造函数又反过来调用子类构造函数了。(其实这个死循环还有办法解决。)

根据上面的分析,我们得出的结论是,DocumentBuilderFactorynewInstance方法实例化了一个子类对象。DocumentBuilderFactory有多少个子类呢,这个我并不清楚,我们可以推测每个子类对象可能是根据外部环境来生成的,比如操作系统类型。

DucumentBuilder这个类也是个抽象类,我们要根据不同的工厂来得到不同的具体子类对象。

3.3 模拟DocumentBuilderFactory

根据对DocumentBuilderFactory的分析,我写了一个程序来模拟它的框架的实现,当然这个实现只是针对它采用抽象工厂模式根据不同的外部环境(这里根据操作系统)来得到不同对象解析器并最终解析Document这一个环节。

下面是对这个模拟的抽象工厂的测试结果,因为是在Window 7下运行,得到的是Win32的解析器对象。

public class MyDocumentFactoryMain {

public static void main(String[] args) {

// TODO Auto-generated method stub

MyDocumentBuilderFactory dbf = MyDocumentBuilderFactory.newIntance();

MyDocumentBuilder db = dbf.newDocumentBuilder();

String document1 = db.parse("document context" );

String document2 = db.newDocument();

System.out.print(document1);

System.out.print(document2);

}

}

程序输出结果:

下面是在Eclipse中的文档结构截图:

MyDocumentBuilderFactory的子类包括MyLinuxDocumentBuilderFactoryMyWin32DocumentBuilderFactoryMyDocumentBuilder的子类包括MyLinuxDocumentBuilderMyWin32DocmentBuilderFactory两个子类。

MyDocumentBuilderFactory.java

package myproject.dom;

public abstract  class MyDocumentBuilderFactory {

private final static int WIN32 = 1;

private final static int LINUX = 2;

public static MyDocumentBuilderFactory newIntance(){

switch(getSystemType()){

case WIN32:

return new MyWin32DocumentBuilderFactory();

case LINUX:

return new MyLinuxDocumentBuilderFactory();

}

return null;

}

static public int getSystemType(){

String osName = System.getProperty("os.name");

System.out.print(osName+"\n");

if(osName.startsWith("Win"))

return WIN32;  //此处应该调用系统API;

else

return LINUX;

}

public abstract MyDocumentBuilder newDocumentBuilder();

}

MyWin32DocumentBuilderFactory.java

package myproject.dom;

public class MyWin32DocumentBuilderFactory extends MyDocumentBuilderFactory {

public  MyDocumentBuilder newDocumentBuilder(){

return new  MyWin32DocumentBuilder();

}

}

MyLinuxDocumentBuilderFactory.java

package myproject.dom;

public class MyLinuxDocumentBuilderFactory extends MyDocumentBuilderFactory {

public  MyDocumentBuilder newDocumentBuilder(){

return new MyLinuxDocumentBuilder();

}

}

MyDocumentBuilder.java

package myproject.dom;

public abstract class MyDocumentBuilder {

public abstract String parse(String context);

public abstract String newDocument();

}

MyLinuxDocumentBuilder.java

package myproject.dom;

public class MyLinuxDocumentBuilder extends MyDocumentBuilder {

@Override

public String parse(String context) {

// TODO Auto-generated method stub

String ret = "\nLinux Builder Parse:Document["+context+"]";

return ret;

}

public String newDocument() {

// TODO Auto-generated method stub

String ret = "\nLinux Builder new:Document[]";

return ret;

}

}

MyWin32DocumentBuilder.java

package myproject.dom;

public class MyWin32DocumentBuilder extends MyDocumentBuilder {

@Override

public String parse(String context) {

// TODO Auto-generated method stub

String ret = "\nWin32 Builder Parse:Document["+context+"]";

return ret;

}

@Override

public String newDocument() {

// TODO Auto-generated method stub

String ret = "\nWin32 Builder new:Document[]";

return ret;

}

}

3.4 SAX模式解析也采用了类似的工厂模式

import javax.xml.parsers.ParserConfigurationException; 

import javax.xml.parsers.SAXParser;   

import javax.xml.parsers.SAXParserFactory;   

import org.xml.sax.Attributes;   

import org.xml.sax.SAXException;  

import org.xml.sax.helpers.DefaultHandler;  

public class SaxDemo implements XmlDocument {  

public void createXml(String fileName) { 

  System.out.println("<<"+filename+">>");  

}  

public void parserXml(String fileName) { 

SAXParserFactory saxfac = SAXParserFactory.newInstance();   

try {  

SAXParser saxparser = saxfac.newSAXParser();  

InputStream is = new FileInputStream(fileName);  

saxparser.parse(is, new MySAXHandler());   

catch (ParserConfigurationException e) { 

  e.printStackTrace();  

catch (SAXException e) {  

e.printStackTrace();  

catch (FileNotFoundException e) {

e.printStackTrace();  

catch (IOException e) { 

e.printStackTrace();  

}   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值