2011-01-28 111 views
57

我正在關注此tutorial如何使用SAX解析器解析XML

它工作的很好,但我希望它返回一個數組與所有的字符串,而不是與最後一個元素的單個字符串。

任何想法如何做到這一點?

+0

能否請您發表您的XML結構的抽象描繪? – 2011-01-28 10:52:56

+0

http://dearfriends.se/category/blog/feed/rss/ - >查看源文件 – Johan 2011-01-28 12:14:30

回答

178

所以你想要構建一個XML解析器來解析一個像這樣的RSS提要。

<rss version="0.92"> 
<channel> 
    <title>MyTitle</title> 
    <link>http://myurl.com</link> 
    <description>MyDescription</description> 
    <lastBuildDate>SomeDate</lastBuildDate> 
    <docs>http://someurl.com</docs> 
    <language>SomeLanguage</language> 

    <item> 
     <title>TitleOne</title> 
     <description><![CDATA[Some text.]]></description> 
     <link>http://linktoarticle.com</link> 
    </item> 

    <item> 
     <title>TitleTwo</title> 
     <description><![CDATA[Some other text.]]></description> 
     <link>http://linktoanotherarticle.com</link> 
    </item> 

</channel> 
</rss> 

現在你有兩個SAX實現你可以使用。您可以使用org.xml.saxandroid.sax實現。我將在發佈一個簡短的例子之後解釋兩者的專業和兼職。

android.sax實施

讓我們先從android.sax實施。

您必須首先使用RootElementElement對象定義XML結構。

在任何情況下,我都會使用POJO(Plain Old Java Objects)來處理您的數據。這將是POJO需要的。

Channel.java

public class Channel implements Serializable { 

    private Items items; 
    private String title; 
    private String link; 
    private String description; 
    private String lastBuildDate; 
    private String docs; 
    private String language; 

    public Channel() { 
     setItems(null); 
     setTitle(null); 
     // set every field to null in the constructor 
    } 

    public void setItems(Items items) { 
     this.items = items; 
    } 

    public Items getItems() { 
     return items; 
    } 

    public void setTitle(String title) { 
     this.title = title; 
    } 

    public String getTitle() { 
     return title; 
    } 
    // rest of the class looks similar so just setters and getters 
} 

這個類實現了Serializable接口,這樣你就可以把它變成一個Bundle並用它做什麼。

現在我們需要一個類來保存我們的項目。在這種情況下,我只想擴展ArrayList類。

Items.java

public class Items extends ArrayList<Item> { 

    public Items() { 
     super(); 
    } 

} 

這就是它爲我們的項目的容器。我們現在需要一個課程來保存每個項目的數據。

Item.java

public class Item implements Serializable { 

    private String title; 
    private String description; 
    private String link; 

    public Item() { 
     setTitle(null); 
     setDescription(null); 
     setLink(null); 
    } 

    public void setTitle(String title) { 
     this.title = title; 
    } 

    public String getTitle() { 
     return title; 
    } 

    // same as above. 

} 

例子:

public class Example extends DefaultHandler { 

    private Channel channel; 
    private Items items; 
    private Item item; 

    public Example() { 
     items = new Items(); 
    } 

    public Channel parse(InputStream is) { 
     RootElement root = new RootElement("rss"); 
     Element chanElement = root.getChild("channel"); 
     Element chanTitle = chanElement.getChild("title"); 
     Element chanLink = chanElement.getChild("link"); 
     Element chanDescription = chanElement.getChild("description"); 
     Element chanLastBuildDate = chanElement.getChild("lastBuildDate"); 
     Element chanDocs = chanElement.getChild("docs"); 
     Element chanLanguage = chanElement.getChild("language"); 

     Element chanItem = chanElement.getChild("item"); 
     Element itemTitle = chanItem.getChild("title"); 
     Element itemDescription = chanItem.getChild("description"); 
     Element itemLink = chanItem.getChild("link"); 

     chanElement.setStartElementListener(new StartElementListener() { 
      public void start(Attributes attributes) { 
       channel = new Channel(); 
      } 
     }); 

     // Listen for the end of a text element and set the text as our 
     // channel's title. 
     chanTitle.setEndTextElementListener(new EndTextElementListener() { 
      public void end(String body) { 
       channel.setTitle(body); 
      } 
     }); 

     // Same thing happens for the other elements of channel ex. 

     // On every <item> tag occurrence we create a new Item object. 
     chanItem.setStartElementListener(new StartElementListener() { 
      public void start(Attributes attributes) { 
       item = new Item(); 
      } 
     }); 

     // On every </item> tag occurrence we add the current Item object 
     // to the Items container. 
     chanItem.setEndElementListener(new EndElementListener() { 
      public void end() { 
       items.add(item); 
      } 
     }); 

     itemTitle.setEndTextElementListener(new EndTextElementListener() { 
      public void end(String body) { 
       item.setTitle(body); 
      } 
     }); 

     // and so on 

     // here we actually parse the InputStream and return the resulting 
     // Channel object. 
     try { 
      Xml.parse(is, Xml.Encoding.UTF_8, root.getContentHandler()); 
      return channel; 
     } catch (SAXException e) { 
      // handle the exception 
     } catch (IOException e) { 
      // handle the exception 
     } 

     return null; 
    } 

} 

現在這是一個非常簡單的例子,你可以看到。使用android.sax SAX實現的主要優點是,您可以定義必須解析的XML的結構,然後將事件偵聽器添加到適當的元素。缺點是代碼變得相當重複和臃腫。

org.xml.sax中實現

org.xml.sax SAX處理程序實現是一個有點不同。

在這裏,您不指定或聲明XML結構,而只是偵聽事件。最廣泛使用的有以下事件:

  • 文檔啓動
  • 文件完
  • 元素開始
  • 元素結束
  • 元素開始和元素結束

一個例子之間的字符使用上面的Channel對象的處理程序實現看起來像這樣。

public class ExampleHandler extends DefaultHandler { 

    private Channel channel; 
    private Items items; 
    private Item item; 
    private boolean inItem = false; 

    private StringBuilder content; 

    public ExampleHandler() { 
     items = new Items(); 
     content = new StringBuilder(); 
    } 

    public void startElement(String uri, String localName, String qName, 
      Attributes atts) throws SAXException { 
     content = new StringBuilder(); 
     if(localName.equalsIgnoreCase("channel")) { 
      channel = new Channel(); 
     } else if(localName.equalsIgnoreCase("item")) { 
      inItem = true; 
      item = new Item(); 
     } 
    } 

    public void endElement(String uri, String localName, String qName) 
      throws SAXException { 
     if(localName.equalsIgnoreCase("title")) { 
      if(inItem) { 
       item.setTitle(content.toString()); 
      } else { 
       channel.setTitle(content.toString()); 
      } 
     } else if(localName.equalsIgnoreCase("link")) { 
      if(inItem) { 
       item.setLink(content.toString()); 
      } else { 
       channel.setLink(content.toString()); 
      } 
     } else if(localName.equalsIgnoreCase("description")) { 
      if(inItem) { 
       item.setDescription(content.toString()); 
      } else { 
       channel.setDescription(content.toString()); 
      } 
     } else if(localName.equalsIgnoreCase("lastBuildDate")) { 
      channel.setLastBuildDate(content.toString()); 
     } else if(localName.equalsIgnoreCase("docs")) { 
      channel.setDocs(content.toString()); 
     } else if(localName.equalsIgnoreCase("language")) { 
      channel.setLanguage(content.toString()); 
     } else if(localName.equalsIgnoreCase("item")) { 
      inItem = false; 
      items.add(item); 
     } else if(localName.equalsIgnoreCase("channel")) { 
      channel.setItems(items); 
     } 
    } 

    public void characters(char[] ch, int start, int length) 
      throws SAXException { 
     content.append(ch, start, length); 
    } 

    public void endDocument() throws SAXException { 
     // you can do something here for example send 
     // the Channel object somewhere or whatever. 
    } 

} 

現在說實話我真的不能告訴你在android.sax一個這樣的處理程序實現的任何真正的優勢。不過,我可以告訴你現在應該顯而易見的缺點。看看startElement方法中的else if語句。由於我們有標籤<title>linkdescription,所以我們必須在目前的XML結構中跟蹤這些標籤。也就是說,如果我們遇到<item>起始標籤,我們將inItem標誌設置爲true以確保我們將正確的數據映射到正確的對象,並且在endElement方法中,如果我們遇到</item>標籤,則將該標誌設置爲false。爲了表明我們已經完成了該物品標籤。

在這個例子中,管理起來非常容易,但必須解析更復雜的結構,並在不同級別重複標記變得棘手。在那裏,你不得不使用Enums來設置你的當前狀態,並且需要很多switch/case statemenets來檢查你的位置,或者更優雅的解決方案是使用標籤堆棧的某種標籤跟蹤器。

2

在許多問題中,有必要爲不同的目的使用不同種類的xml文件。我不會試圖去掌握無限的,並從我自己的經驗中得知我所需要的一切。可能是我最喜歡的編程語言。此外,這種愛情是通過你可以解決任何問題,並提出一個自行車沒有必要的事實加強。

所以,我花了很多時間創建一個運行數據庫的客戶端服務器,讓客戶端可以遠程在數據庫服務器上創建條目。不用檢查輸入數據等等,但不是那樣的。

作爲工作原理,我毫不猶豫地選擇了以xml文件形式傳輸信息。以下幾種類型:

<? xml version = "1.0" encoding = "UTF-8" standalone = "no"?> 
<doc> 
<id> 3 </ id> 
<fam> Ivanov </ fam> 
<name> Ivan </ name> 
<otc> I. </ otc> 
<dateb> 10-03-2005 </ dateb> 
<datep> 10-03-2005 </ datep> 
<datev> 10-03-2005 </ datev> 
<datebegin> 09-06-2009 </ datebegin> 
<dateend> 10-03-2005 </ dateend> 
<vdolid> 1 </ vdolid> 
<specid> 1 </ specid> 
<klavid> 1 </ klavid> 
<stav> 2.0 </ stav> 
<progid> 1 </ progid> 
</ doc> 

讓它更易於閱讀,除非說它是關於醫生機構的信息。姓氏,名字,唯一ID等等。一般來說,數據系列。該文件安全地在服務器端,然後開始解析文件。

兩個選項解析(SAX VS DOM)我選擇的是他的作品更亮的事實SAX視圖,他是第一個我掉進手中:)

所以。如您所知,爲了成功解析解析器,我們需要重寫所需的方法DefaultHandler。首先,連接所需的軟件包。

import org.xml.sax.helpers.DefaultHandler; 
import org.xml.sax. *; 

現在我們可以開始寫我們的分析器

public class SAXPars extends DefaultHandler { 
   ... 
} 

讓我們先從方法startDocument()。顧名思義,他對文檔開始的事件作出反應。在這裏,你可以掛各種如內存分配行爲,或重置價值,但我們的例子是非常簡單的,所以只是標誌着一個適當的消息開始工作:

Override 
public void startDocument() throws SAXException { 
   System.out.println ("Start parse XML ..."); 
} 

下一步。解析器遍歷文檔符合其結構的元素。啓動方法startElement()。而事實上,他的外表如下:startElement(String namespaceURI,String localName,String qName,Attributes atts)。這裏namespaceURI - 命名空間,localName - 元素的本地名稱,qName - 本地名稱與命名空間的組合(用冒號分隔)和atts - 此元素的屬性。在這種情況下,所有的簡單。只需使用qName'om並將其放入某個服務行thisElement就足夠了。因此,我們標記出我們目前的元素。

@Override 
public void startElement (String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { 
   thisElement = qName; 
} 

接下來,我們看到會議項目的含義。這裏包括方法characters()。他具有以下形式:字符(char [] ch,int start,int length)。那麼這裏一切都很清楚。 ch - 這個元素中包含字符串本身自身重要性的文件。開始和長度 - 指示行和起始點的服務數量和長度。

@Override 
public void characters (char [] ch, int start, int length) throws SAXException { 
   if (thisElement.equals ("id")) { 
      doc.setId (new Integer (new String (ch, start, length))); 
   } 
   if (thisElement.equals ("fam")) { 
      doc.setFam (new String (ch, start, length)); 
   } 
   if (thisElement.equals ("name")) { 
      doc.setName (new String (ch, start, length)); 
   } 
   if (thisElement.equals ("otc")) { 
      doc.setOtc (new String (ch, start, length)); 
   } 
   if (thisElement.equals ("dateb")) { 
      doc.setDateb (new String (ch, start, length)); 
   } 
   if (thisElement.equals ("datep")) { 
      doc.setDatep (new String (ch, start, length)); 
   } 
   if (thisElement.equals ("datev")) { 
      doc.setDatev (new String (ch, start, length)); 
   } 
   if (thisElement.equals ("datebegin")) { 
      doc.setDatebegin (new String (ch, start, length)); 
   } 
   if (thisElement.equals ("dateend")) { 
      doc.setDateend (new String (ch, start, length)); 
   } 
   if (thisElement.equals ("vdolid")) { 
      doc.setVdolid (new Integer (new String (ch, start, length))); 
   } 
   if (thisElement.equals ("specid")) { 
      doc.setSpecid (new Integer (new String (ch, start, length))); 
   } 
   if (thisElement.equals ("klavid")) { 
      doc.setKlavid (new Integer (new String (ch, start, length))); 
   } 
   if (thisElement.equals ("stav")) { 
      doc.setStav (new Float (new String (ch, start, length))); 
   } 
   if (thisElement.equals ("progid")) { 
      doc.setProgid (new Integer (new String (ch, start, length))); 
   } 
} 

啊,是的。我差點忘了。作爲摺疊Naparsennye數據的對象將與醫生的類型進行對照。這個類被定義並具有所有必要的setter-getters。

下一個明顯的元素結束,接下來是下一個。負責結束endElement()。它向我們表明該項目已經結束,您現在可以做任何事情。將繼續。清潔元素。

@Override 
public void endElement (String namespaceURI, String localName, String qName) throws SAXException { 
   thisElement = ""; 
} 

即將到來的整個文件,我們來到文件的末尾。工作結束文檔()。在它裏面,我們可以釋放內存,做一些診斷,打印等等。在我們的例子中,只要寫下解析結束的內容即可。

@Override 
public void endDocument() { 
   System.out.println ("Stop parse XML ..."); 
} 

所以我們有一個類來解析xml我們的格式。以下是全文:

import org.xml.sax.helpers.DefaultHandler; 
import org.xml.sax. *; 
  
public class SAXPars extends DefaultHandler { 
  
Doctors doc = new Doctors(); 
String thisElement = ""; 
  
public Doctors getResult() { 
   return doc; 
} 
  
@Override 
public void startDocument() throws SAXException { 
   System.out.println ("Start parse XML ..."); 
} 
  
@Override 
public void startElement (String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { 
   thisElement = qName; 
} 
  
@Override 
public void endElement (String namespaceURI, String localName, String qName) throws SAXException { 
   thisElement = ""; 
} 
  
@Override 
public void characters (char [] ch, int start, int length) throws SAXException { 
   if (thisElement.equals ("id")) { 
      doc.setId (new Integer (new String (ch, start, length))); 
   } 
   if (thisElement.equals ("fam")) { 
      doc.setFam (new String (ch, start, length)); 
   } 
   if (thisElement.equals ("name")) { 
      doc.setName (new String (ch, start, length)); 
   } 
   if (thisElement.equals ("otc")) { 
      doc.setOtc (new String (ch, start, length)); 
   } 
   if (thisElement.equals ("dateb")) { 
      doc.setDateb (new String (ch, start, length)); 
   } 
   if (thisElement.equals ("datep")) { 
      doc.setDatep (new String (ch, start, length)); 
   } 
   if (thisElement.equals ("datev")) { 
      doc.setDatev (new String (ch, start, length)); 
   } 
   if (thisElement.equals ("datebegin")) { 
      doc.setDatebegin (new String (ch, start, length)); 
   } 
   if (thisElement.equals ("dateend")) { 
      doc.setDateend (new String (ch, start, length)); 
   } 
   if (thisElement.equals ("vdolid")) { 
      doc.setVdolid (new Integer (new String (ch, start, length))); 
   } 
   if (thisElement.equals ("specid")) { 
      doc.setSpecid (new Integer (new String (ch, start, length))); 
   } 
   if (thisElement.equals ("klavid")) { 
      doc.setKlavid (new Integer (new String (ch, start, length))); 
   } 
   if (thisElement.equals ("stav")) { 
      doc.setStav (new Float (new String (ch, start, length))); 
   } 
   if (thisElement.equals ("progid")) { 
      doc.setProgid (new Integer (new String (ch, start, length))); 
   } 
} 
  
@Override 
public void endDocument() { 
   System.out.println ("Stop parse XML ..."); 
} 
} 

我希望主題有助於很容易地提供SAX解析器的本質。

不要嚴格判斷第一篇文章:)我希望這是至少有用的。

UPD:要運行這個分析器,您可以使用此代碼:

SAXParserFactory factory = SAXParserFactory.newInstance(); 
SAXParser parser = factory.newSAXParser(); 
SAXPars saxp = new SAXPars(); 
  
parser.parse (new File ("..."), saxp);