android xml 解析 修改

时间:2022-11-22 12:37:56

解析

在Android中提供了三种解析XML的方式:SAX(Simple API XML),DOM(Document Objrect Model),以及Android推荐的Pull解析方式.下面就对三种解析方式一一详细阐述。

假设要要解析person.xml文档

<?xml version="1.0" encoding="UTF-8"?>
<persons>
  <person id="1">
    <name>zhangsan</name>
    <age>21</age>
  </person>
  <person id="2">
    <name>lisi</name>
    <age>22</age>
  </person>
  <person id="3">
    <name>wangwu</name>
    <age>222</age>
  </person>
</persons>

 

 

 

   首先介绍SAX解析,SAX是事件驱动型XML解析的一个标准接口不会改变 SAX的工作原理简单地说就是对文档进行顺序扫描,当扫描到文档(document)开始与结束、元素(element)开始与结束、文档(document)结束等地方时通知事件处理函数,由事件处理函数做相应动作,然后继续同样的扫描,直至文档结束。下面结合代码分析

public class SAXPersonService {

 public List<Person> getPersons (InputStream instream) throws Exception{
  SAXParserFactory factory = SAXParserFactory.newInstance();//创建SAX解析工厂
  SAXParser paser = factory.newSAXParser();//创建SAX解析器
  PersonPaser personPaser=new PersonPaser();//创建事件处理程序
  paser.parse(instream,personPaser);//开始解析
  instream.close();//关闭输入流
  return personPaser.getPersons();//返回解析后的内容
  
 }
 public final class PersonPaser extends DefaultHandler{//创建事件处理程序,
也就是编写ContentHandler的实现类,一般继承自DefaultHandler类

public List<Person> getPersons() {
return persons;
}
  private List<Person> persons=null;
  private String tagName=null;
  private Person person=null;

{

//遇到文档开始标记的时候创建person集合
        public void startDocument() throws SAXException          persons=new ArrayList<Person>();
  }
  //遇到元素节点开始时候的处理方法
  public void startElement(String uri, String localName, String qName,
    Attributes attributes) throws SAXException {
   tagName = localName;

  //如果遇到<person>标记,则创建一个person
   if("person".equals(tagName)){
     person = new Person();
     person.setId(new Integer(attributes.getValue(0)));//取出标记内的属性
   } 

}

  //遇到文本节点时的操作

  public void characters(char[] ch, int start, int length)
    throws SAXException {
   if(tagName!=null){//文本节点必须前面要有元素节点开始标记
    String data = new String(ch,start,length);//取出文本节点的值
    if("name".equals(tagName)){//如果前面的元素节点开始标记是name
     person.setName(data);//则将文本节点的值赋值给person的Name
    }else if("age".equals(tagName)){//如果前面元素节点开始标记是age
     person.setAge(new Short(data));//则将本节点的值赋值给person的Age
    }
   }

  }

 //遇到元素节点结束时候的操作
  public void endElement(String uri, String localName, String qName)
    throws SAXException {
   if("person".equals(localName)){//如果遇到</person>标记
    persons.add(person);//则将创建完成的person加入到集合中去
    person=null;//置空下一个person
   }
   tagName=null;//置空已有标记,因为要解析下一个节点了
  }
 }

  至此,SAX解析完毕!

 

 

下面介绍DOM解析,DOM,即对象文档模型,它是将整个XML文档载入内存(所以效率较低,不推荐使用),每一个节点当做一个对象,结合代码分析

public class DomPersonService {

 public List<Person> getPersons (InputStream instream) throws Exception{
  List<Person> persons = new ArrayList<Person>();
  DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();//创建DOM解析工厂
  DocumentBuilder dombuild = factory.newDocumentBuilder();//创建DON解析器
  Document dom = dombuild.parse(instream);//开始解析XML文档并且得到整个文档的对象模型
  Element root= dom.getDocumentElement();//得到根节点<persons>
  NodeList personList = root.getElementsByTagName_r("person");//得到根节点下所有标签为<person>的子节点
  for(int i = 0;i<personList.getLength();i++){//遍历person节点
    Person person = new Person();//首先创建一个Person
    Element personElement = (Element) personList.item(i);//得到本次Person元素节点
    person.setId(new Integer(personElement.getAttribute("id")));//得到Person节点中的ID

    NodeList personChilds = personElement.getChildNodes();//得到Person节点下的所有子节点
    for(int j=0;j<personChilds.getLength();j++){//遍历person节点下的所有子节点
      if(personChilds.item(j).getNodeType()==Node.ELEMENT_NODE){//如果是元素节点的话
       Element childElement  = (Element) personChilds.item(j); //得到该元素节点
       if("name".equals(childElement.getNodeName())){//如果该元素节点是name节点
        person.setName(childElement.getFirstChild().getNodeValue());//得到name节点下的第一个文本子节点的值
       }else if("age".equals(childElement.getNodeName())){//如果该元素节点是age节点、

        person.setAge(new Short(childElement.getFirstChild().getNodeValue()));//得到age节点下的第一个文本字节点的值

       }
      }
    }
    persons.add(person);//遍历完person下的所有子节点后将person元素加入到集合中去
  }
  return persons;
 }

 

 

 至此,DOM解析方式结束!

 

 

 下面介绍Pull解析

 public class PulPersonService {

 public List<Person> getPersons(InputStream instream) throws Exception {
  List<Person> persons = null;
  Person person = null;
  XmlPullParser parser = Xml.newPullParser();//得到Pull解析器
  parser.setInput(instream, "UTF-8");//设置下输入流的编码
  int eventType = parser.getEventType();//得到第一个事件类型
  while (eventType != XmlPullParser.END_DOCUMENT) {//如果事件类型不是文档结束的话则不断处理事件
   switch (eventType) {
   case (XmlPullParser.START_DOCUMENT)://如果是文档开始事件
    persons = new ArrayList<Person>();创建一个person集合
    break;
   case (XmlPullParser.START_TAG)://如果遇到标签开始

    String tagName = parser.getName();// 获得解析器当前元素的名称
    if ("person".equals(tagName)) {//如果当前标签名称是<person>
     person = new Person();//创建一个person
     person.setId(new Integer(parser.getAttributeValue(0)));//将元素的属性值赋值给id
    }
    if (person != null) {//如果person已经创建完成
     if ("name".equals(tagName))//如果当前节点标记是name
      person.setName(new String(parser.nextText()));
     else if ("age".equals(tagName))//如果当前元素节点标记是age
      person.setAge(new Short(parser.nextText()));
    }
   break;
   case (XmlPullParser.END_TAG)://如果遇到标签结束

    if ("person".equals(parser.getName())) {//如果是person标签结束
     persons.add(person);//将创建完成的person加入集合
     person = null;//并且置空
    }
   break;
   }
   eventType=parser.next();//进入下一个事件处理
  }
        return persons;
 }

修改

1:用DOM 解析 形成 节点列表,节点内容与内存地址映射,改节点内容就是修改 内存中的 Document 对象
然后使用 Document 对象(在内存中已经经过修改):
document 是 Document document;

TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer  transformer =  transformerFactory.newTransformer();
Source xmlSource = new DOMSource(document);
Result outputTarget = new StreamResult(new File("F:\\javaweb20120208_workspace\\day3\\orders.xml"));//xm文件所在位置 transformer.transform(xmlSource, outputTarget);//提交覆盖原来的xml

2:用 SAX 或PULL 解析 生成实体列表,LIST<类> 实体列表名
然后 修改 列表中的对应的实体。
使用修改后的实体列表 用 XmlSerializer 序列化 写回原地址的XML文件 覆盖

例子如下:

     * @return 生成的xml文件的字符串表示 
     */  
    private String produceXml(){  
          
        StringWriter stringWriter = new StringWriter();  
        ArrayList<Beauty> beautyList = getData();  
        try {  
            // 获取XmlSerializer对象  
            XmlPullParserFactory factory = XmlPullParserFactory.newInstance();  
            XmlSerializer xmlSerializer = factory.newSerializer();  
            // 设置输出流对象  
            xmlSerializer.setOutput(stringWriter);  

            xmlSerializer.startDocument("utf-8", true);  
            xmlSerializer.startTag(null, "beauties");  
            for(Beauty beauty:beautyList){  

                xmlSerializer.startTag(null, "beauty");  
                  
                xmlSerializer.startTag(null, "name");  
                xmlSerializer.text(beauty.getName());  
                xmlSerializer.endTag(null, "name");  
                  
                xmlSerializer.startTag(null, "age");  
                xmlSerializer.text(beauty.getAge());  
                xmlSerializer.endTag(null, "age");  
                  
                xmlSerializer.endTag(null, "beauty");  
            }  
            xmlSerializer.endTag(null, "beauties");  
            xmlSerializer.endDocument();  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
        return stringWriter.toString();  
  
    }  



工具

在项目中,我们很多都用到了xml文件,无论是参数配置还是与其它系统的数据交互。
今天就来讲一下Java 中使用dom4j来操作XML文件。

我们需要引入的包:

//文件包 
import java.io.ByteArrayOutputStream; 
import java.io.File; 
import java.io.FileWriter; 
//工具包 
import java.util.Iterator; 
import java.util.List; 
//dom4j包 
import org.dom4j.Attribute; 
import org.dom4j.Document; 
import org.dom4j.DocumentHelper; 
import org.dom4j.Element; 
import org.dom4j.io.OutputFormat; 
import org.dom4j.io.SAXReader; 
import org.dom4j.io.XMLWriter;

1、将XML文件的内容转化为String

   /** 
    * doc2String 
    * 将xml文档内容转为String 
    * @return 字符串 
    * @param document 
    */ 
   public static String doc2String(Document document) 
   { 
      String s = ""; 
      try 
      { 
           //使用输出流来进行转化 
           ByteArrayOutputStream out = new ByteArrayOutputStream(); 
           //使用GB2312编码 
           OutputFormat format = new OutputFormat("  ", true, "GB2312"); 
           XMLWriter writer = new XMLWriter(out, format); 
           writer.write(document); 
           s = out.toString("GB2312"); 
      }catch(Exception ex) 
      {             
           ex.printStackTrace(); 
      }       
      return s; 
   }

2、将符合XML格式的String 转化为XML Document

   /** 
    * string2Document 
    * 将字符串转为Document 
    * @return  
    * @param s xml格式的字符串 
    */ 
   public static Document string2Document(String s) 
   { 
      Document doc = null; 
      try 
      { 
           doc = DocumentHelper.parseText(s); 
      }catch(Exception ex) 
      {             
           ex.printStackTrace(); 
      } 
      return doc; 
   }

3、将Document对象保存为一个xml文件到本地

   /** 
    * doc2XmlFile 
    * 将Document对象保存为一个xml文件到本地 
    * @return true:保存成功  flase:失败 
    * @param filename 保存的文件名 
    * @param document 需要保存的document对象 
    */ 
   public static boolean doc2XmlFile(Document document,String filename) 
   { 
      boolean flag = true; 
      try 
      { 
            /* 将document中的内容写入文件中 */ 
            //默认为UTF-8格式,指定为"GB2312" 
            OutputFormat format = OutputFormat.createPrettyPrint(); 
            format.setEncoding("GB2312"); 
            XMLWriter writer = new XMLWriter(new FileWriter(new File(filename)),format); 
            writer.write(document); 
            writer.close();             
        }catch(Exception ex) 
        { 
            flag = false; 
            ex.printStackTrace(); 
        } 
        return flag;       
   }

4、将xml格式的字符串保存为本地文件,如果字符串格式不符合xml规则,则返回失败

   /** 
    * string2XmlFile 
    * 将xml格式的字符串保存为本地文件,如果字符串格式不符合xml规则,则返回失败 
    * @return true:保存成功  flase:失败 
    * @param filename 保存的文件名 
    * @param str 需要保存的字符串 
    */ 
   public static boolean string2XmlFile(String str,String filename) 
   { 
      boolean flag = true; 
      try 
      { 
         Document doc =  DocumentHelper.parseText(str);        
         flag = doc2XmlFile(doc,filename); 
      }catch (Exception ex) 
      { 
         flag = false; 
         ex.printStackTrace(); 
      } 
      return flag; 
   }

5、载入一个xml文档

   /** 
    * load 
    * 载入一个xml文档 
    * @return 成功返回Document对象,失败返回null 
    * @param uri 文件路径 
    */ 
   public static Document load(String filename) 
   { 
      Document document = null; 
      try  
      {  
          SAXReader saxReader = new SAXReader(); 
          document = saxReader.read(new File(filename)); 
      } 
      catch (Exception ex){ 
          ex.printStackTrace(); 
      }   
      return document; 
   }

 6、演示String保存为xml文件

   /** 
    * xmlWriteDemoByString 
    * 演示String保存为xml文件 
    */ 
   public void xmlWriteDemoByString() 
   { 
      String s = ""; 
      /** xml格式标题 "<?xml version='1.0' encoding='GB2312'?>" 可以不用写*/ 
      s = "<config>\r\n" 
         +"   <ftp name='DongDian'>\r\n" 
         +"     <ftp-host>127.0.0.1</ftp-host>\r\n" 
         +"     <ftp-port>21</ftp-port>\r\n" 
         +"     <ftp-user>cxl</ftp-user>\r\n" 
         +"     <ftp-pwd>longshine</ftp-pwd>\r\n" 
         +"     <!-- ftp最多尝试连接次数 -->\r\n" 
         +"     <ftp-try>50</ftp-try>\r\n" 
         +"     <!-- ftp尝试连接延迟时间 -->\r\n" 
         +"     <ftp-delay>10</ftp-delay>\r\n" 
         +"  </ftp>\r\n" 
         +"</config>\r\n"; 
      //将文件生成到classes文件夹所在的目录里    
      string2XmlFile(s,"xmlWriteDemoByString.xml");    
      //将文件生成到classes文件夹里    
      string2XmlFile(s,"classes/xmlWriteDemoByString.xml");   
   }

7、演示手动创建一个Document,并保存为XML文件

   /** 
    * 演示手动创建一个Document,并保存为XML文件 
    */ 
   public void xmlWriteDemoByDocument() 
   { 
        /** 建立document对象 */ 
        Document document = DocumentHelper.createDocument(); 
        /** 建立config根节点 */ 
        Element configElement = document.addElement("config"); 
        /** 建立ftp节点 */ 
        configElement.addComment("东电ftp配置"); 
        Element ftpElement = configElement.addElement("ftp"); 
        ftpElement.addAttribute("name","DongDian"); 
        /** ftp 属性配置 */ 
        Element hostElement = ftpElement.addElement("ftp-host"); 
        hostElement.setText("127.0.0.1"); 
        (ftpElement.addElement("ftp-port")).setText("21"); 
        (ftpElement.addElement("ftp-user")).setText("cxl"); 
        (ftpElement.addElement("ftp-pwd")).setText("longshine"); 
        ftpElement.addComment("ftp最多尝试连接次数"); 
        (ftpElement.addElement("ftp-try")).setText("50"); 
        ftpElement.addComment("ftp尝试连接延迟时间"); 
        (ftpElement.addElement("ftp-delay")).setText("10");     
        /** 保存Document */ 
        doc2XmlFile(document,"classes/xmlWriteDemoByDocument.xml"); 
   }

8、演示读取文件的具体某个节点的值

   /** 
    *  演示读取文件的具体某个节点的值  
    */ 
   public static void xmlReadDemo() 
   { 
      Document doc = load("classes/xmlWriteDemoByDocument.xml"); 
      //Element root = doc.getRootElement(); 
      /** 先用xpath查找所有ftp节点 并输出它的name属性值*/ 
      List list = doc.selectNodes("/config/ftp" ); 
      Iterator it = list.iterator(); 
      while(it.hasNext()) 
      {    
          Element ftpElement = (Element)it.next(); 
          System.out.println("ftp_name="+ftpElement.attribute("name").getValue()); 
      } 
      /** 直接用属性path取得name值 */ 
      list = doc.selectNodes("/config/ftp/@name" ); 
      it = list.iterator(); 
      while(it.hasNext()) 
      {    
          Attribute attribute = (Attribute)it.next(); 
          System.out.println("@name="+attribute.getValue()); 
      } 
      /** 直接取得DongDian ftp的 ftp-host 的值 */ 
      list = doc.selectNodes("/config/ftp/ftp-host" ); 
      it = list.iterator(); 
      Element hostElement=(Element)it.next(); 
      System.out.println("DongDian's ftp_host="+hostElement.getText()); 
   }

9、修改或删除某个值或属性

/** ftp节点删除ftp-host节点 */ 
ftpElement.remove(hostElement);  
/** ftp节点删除name属性 */ 
ftpElement.remove(nameAttribute); 
/** 修改ftp-host的值 */ 
hostElement.setText("192.168.0.1");  
/** 修改ftp节点name属性的值 */ 
nameAttribute.setValue("ChiFeng");