如何使用 SAX 解析器解析 XML

2022-08-31 17:06:11

我正在学习本教程

它工作得很好,但我希望它返回一个包含所有字符串的数组,而不是一个包含最后一个元素的单个字符串。

任何想法如何做到这一点?


答案 1

因此,您希望构建一个 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 Implementation

让我们从实现开始。android.sax

首先必须使用 和 对象定义 XML 结构。RootElementElement

无论如何,我会使用POJO(普通的旧Java对象),它将保存您的数据。这将是所需的POJO。

通道.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
}

此类实现接口,因此您可以将其放入 a 中并对其执行某些操作。SerializableBundle

现在我们需要一个类来保存我们的项目。在本例中,我将扩展该类。ArrayList

项目.java

public class Items extends ArrayList<Item> {

    public Items() {
        super();
    }

}

这就是我们的物品容器。我们现在需要一个类来保存每个项目的数据。

项目.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;
    }

}

正如你所看到的,这是一个非常快速的例子。使用 SAX 实现的主要优点是,您可以定义必须解析的 XML 结构,然后只需将事件侦听器添加到相应的元素即可。缺点是代码变得非常重复和臃肿。android.sax

org.xml.sax Implementation

SAX 处理程序实现略有不同。org.xml.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.
    }

}

现在说实话,我不能真正告诉你这个处理程序实现相对于这个处理程序实现的任何真正优势。然而,我可以告诉你缺点,现在应该很明显。查看方法中的 else if 语句。由于我们有 标记 ,并且我们必须在XML结构中跟踪,因此我们目前处于状态。也就是说,如果我们遇到一个起始标记,我们将标志设置为确保将正确的数据映射到正确的对象,并且在遇到标记时将该标志设置为该标志的方法。表明我们已经完成了该项目标签。android.saxstartElement<title>linkdescription<item>inItemtrueendElementfalse</item>

在此示例中,管理它非常容易,但是必须解析具有不同级别的重复标记的更复杂的结构变得棘手。在那里,您必须使用枚举来设置当前状态,并使用许多开关/案例状态来检查您的位置,或者更优雅的解决方案是使用标签堆栈的某种标签跟踪器。


答案 2

在许多问题中,有必要将不同类型的xml文件用于不同的目的。我不会试图抓住浩瀚,从我自己的经验中说出我需要什么。

Java,也许是我最喜欢的编程语言。此外,这种爱因您可以解决任何问题而得到加强,并且不需要提出自行车。

因此,我花了很长时间才创建了一堆运行数据库的客户端-服务器,该数据库将允许客户端在数据库服务器中远程创建条目。无需检查输入数据等,但这不是关于那个的。

作为工作原则,我毫不犹豫地选择了以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的观点,即他工作得更明亮,他是我第一个落入手中的人:)

所以。如您所知,要成功使用解析器,我们需要覆盖默认处理程序所需的方法。首先,连接所需的包。

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(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; 
} 

接下来,会议项目我们了解其含义。此处包括方法字符 ()。他有形式:字符(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-getter。

下一个明显的元素结束,然后是下一个。负责结束 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); 


推荐