如何使用 SAX 解析器解析名称空间?

Posted

技术标签:

【中文标题】如何使用 SAX 解析器解析名称空间?【英文标题】:How can I parse a namespace using the SAX parser? 【发布时间】:2011-03-29 21:10:42 【问题描述】:

使用 twitter 搜索 URL 即。 http://search.twitter.com/search.rss?q=android 返回的 CSS 具有如下所示的项目:

<item>
      <title>@UberTwiter still waiting for @ubertwitter  android app!!!</title>
      <link>http://twitter.com/meals69/statuses/21158076391</link>
      <description>still waiting for an app!!!</description>
      <pubDate>Sat, 14 Aug 2010 15:33:44 +0000</pubDate>
      <guid>http://twitter.com/meals69/statuses/21158076391</guid>
      <author>Some Twitter User</author>
      <media:content type="image/jpg"   url="http://a1.twimg.com/profile_images/756343289/me2_normal.jpg"/>
      <google:image_link>http://a1.twimg.com/profile_images/756343289/me2_normal.jpg</google:image_link>
      <twitter:metadata>
        <twitter:result_type>recent</twitter:result_type>
</twitter:metadata>
</item>

很简单。我的代码解析出所有内容(标题、链接、描述、pubDate 等),没有任何问题。但是,我得到了空值:

<google:image_link>

我正在使用 Java 来解析 RSS 提要。与更简单的本地名称相比,我是否必须以不同的方式处理复合本地名称?

这是解析出 Link、Description、pubDate 等的代码:

@Override
    public void endElement(String uri, String localName, String name)
            throws SAXException 
        super.endElement(uri, localName, name);
        if (this.currentMessage != null)
            if (localName.equalsIgnoreCase(TITLE))
                currentMessage.setTitle(builder.toString());
             else if (localName.equalsIgnoreCase(LINK))
                currentMessage.setLink(builder.toString());
             else if (localName.equalsIgnoreCase(DESCRIPTION))
                currentMessage.setDescription(builder.toString());
             else if (localName.equalsIgnoreCase(PUB_DATE))
                currentMessage.setDate(builder.toString());
             else if (localName.equalsIgnoreCase(GUID))
                currentMessage.setGuid(builder.toString());
             else if (uri.equalsIgnoreCase(AVATAR))
                currentMessage.setAvatar(builder.toString());
             else if (localName.equalsIgnoreCase(ITEM))
                messages.add(currentMessage);
             
            builder.setLength(0);   
        
    

startDocument 看起来像:

@Override
    public void startDocument() throws SAXException 
        super.startDocument();
        messages = new ArrayList<Message>();
        builder = new StringBuilder();

    

startElement 看起来像:

@Override
    public void startElement(String uri, String localName, String name,
            Attributes attributes) throws SAXException 
        super.startElement(uri, localName, name, attributes);
        if (localName.equalsIgnoreCase(ITEM))
            this.currentMessage = new Message();
         
    

托尼

【问题讨论】:

你能解释一下“但是,我在&lt;google:image_link&gt; 上得到空值”是什么意思吗? 【参考方案1】:

使用我的 xml 处理程序的 startPrefixMapping 方法,我能够解析出命名空间的文本。

我在处理程序实例下对该方法进行了多次调用。

GoogleReader xmlhandler = new GoogleReader();
xmlhandler.startPrefixMapping("dc", "http://purl.org/dc/elements/1.1/");

其中 dc 是命名空间 &lt;dc:author&gt;some text&lt;/dc:author&gt;

【讨论】:

【参考方案2】:

可能会帮助使用Android SAX util 的人。我正在尝试 geo:lat 从地理命名空间中获取 lat 元素。

示例 XML:

<item> 
 <title>My Item title</title> 
 <geo:lat>40.720741</geo:lat> 
</item>

第一次尝试返回 null:

item.getChild("geo:lat");

如上所述,我发现将命名空间 URI 传递给 getChild 方法是可行的。

item.getChild("http://www.w3.org/2003/01/geo/wgs84_pos#", "lat");

【讨论】:

【参考方案3】:

就像用户 polygenelubricants 所说:通常解析器需要知道命名空间来处理属于某个前缀命名空间的元素。 (就像&lt;google:image_link&gt; 元素一样。)

这需要设置为“解析器功能”,AFAIK 可以通过几种不同的方式完成:XMLReader 接口本身具有方法setFeature(),可用于为某个解析器设置功能,但您也可以使用相同的SAXParserFactory 类的方法,以便该工厂生成已打开或关闭这些功能的解析器。 SAX2 标准功能标志应该在 SAXproject 的网站上,但至少其中一些也列在包 org.xml.sax 的 Java API 文档中。

对于简单的文档,您可以尝试走捷径。如果您实际上并不关心 URL + 本地名称组合中的名称空间和元素名称,并且您可以相信您正在寻找的元素(并且只有这些)总是具有特定的前缀并且没有来自的元素其他具有相同本地名称的命名空间,那么您可以通过使用 startElement() 方法的 qname 参数而不是 localName 或反之亦然或通过添加/删除您比较的标签名称字符串中的前缀来解决您的问题。

参数namespaceUriqnamelocalName 的内容根据Java 规范实际上是可选的,因此AFAIK 可能是null。其中哪些是null 取决于前面提到的那些影响命名空间的“解析器功能”。我不知道 null 的参数是否会在命名空间中的元素和没有命名空间的元素之间有所不同 - 我没有调查过这种行为。

PS。 XML 区分大小写。所以理想情况下,您不需要在标签名称字符串比较中忽略大小写。-第一篇文章,耶!

【讨论】:

【参考方案4】:

从示例中,实际上并不清楚“google”前缀绑定到哪个命名空间——之前的答案有点不正确,因为它不在“google”命名空间中;相反,它是前缀“google”绑定到的命名空间。因此,您必须匹配命名空间(由 URI 标识),而不是前缀。 SAX 报告本地名称/命名空间前缀组合的方式确实令人困惑,这取决于是否启用了命名空间处理。

您还可以考虑替代 XML 处理库/API;虽然 SAX 实现是高性能的,但也有更快、更方便的替代方案。像 Woodstox(甚至是 JDK 1.6 附带的默认实现)这样的 Stax (javax.xml.stream.*) 实现既快速又方便。并且在 Stax 之上构建的 StaxMate 库在读取和写入时使用起来要简单得多,而且速度与 Xerces 等 SAX 实现一样快。此外,Stax API 对命名空间的处理更少,因此更容易查看元素的实际命名空间。

【讨论】:

+1,感谢您的更正。我允许您编辑我的答案以纠正任何错误,或者只是将部分内容提取到您自己的内容中并进行更正等。 谢谢。我似乎无权进行编辑,但我认为只需更改措辞以提及间接性就可以了?【参考方案5】:

&lt;google:image_link&gt; 这样的元素具有属于google 命名空间的本地名称image_link。您需要确保 XML 解析框架知道名称空间,然后您需要使用适当的名称空间来查找此元素。

例如,package org.xml.sax 中的一些 SAX1 接口已被弃用,取而代之的是包含命名空间支持的 SAX2 对应接口(例如,SAX1 Parser 已弃用并由 SAX2 XMLReader 取代)。请参阅有关如何指定命名空间 uri 或限定(前缀)qName 的文档。

另见

Wikipedia/XML namespace package org.xml.sax saxproject.org - Namespaces

【讨论】:

我正在使用 SAX 解析框架(我相信)。我是 Java 新手。 @Silvestri:你能添加一些代码 sn-p 来展示你是如何做到这一点的吗? 刚刚添加了一些代码 sn-ps,现在正在阅读文档。仍然不清楚我将如何完成这项工作,但我会认为这非常简单。 @Silvestri:我认为有很多方法可以更容易地做到这一点,例如XPath,或 Apache 的 Digester。请放心,我仍在努力。 @Silvestri:查看 XPath 方法:ideone.com/UqkQU;还可以查看 Apache 的 Digester,它也会使代码更简单。明天我会回到这个,希望到那时其他人也会给你很好的答案。

以上是关于如何使用 SAX 解析器解析名称空间?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 C# 中使用事件驱动的 SAX(Simple API FOR XML) 解析器,还是使用 System.XML 命名空间更好?

如何使用 SAX 解析器解析 XML

使用 SAX 解析器时如何获取父节点?

如何使用 SAX 解析器在 XML 中添加元素?

如何使用标准库包名称解决 Python 包中的命名空间冲突?

如果输入文件中未指定 DTD,如何强制 SAX 解析器使用 DTD?