如何使用 SAX 解析器解析 XML

2024-02-07

我正在关注这个tutorial http://www.anddev.org/parsing_xml_from_the_net_-_using_the_saxparser-t353.html.

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

有什么想法如何做到这一点?


因此,您想要构建一个 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.sax or the android.sax执行。在发布一个短手示例后,我将解释两者的优缺点。

android.sax 实现

让我们从android.sax执行。

您首先必须使用以下命令定义 XML 结构RootElement and Element对象。

无论如何,我会使用 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
}

这个类实现了Serializable接口,以便您可以将其放入Bundle并用它做一些事情。

现在我们需要一个类来保存我们的项目。在这种情况下,我只是要延长ArrayList class.

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

}

Example:

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.saxSAX 实现是您可以定义必须解析的 XML 的结构,然后只需将事件侦听器添加到适当的元素即可。缺点是代码变得相当重复且臃肿。

org.xml.sax 实现

The org.xml.saxSAX 处理程序的实现有点不同。

在这里,您不指定或声明 XML 结构,而只是监听事件。最广泛使用的是以下事件:

  • 文档开始
  • 文件结束
  • 元素开始
  • 元素结束
  • 元素开始和元素结束之间的字符

使用上面的 Channel 对象的示例处理程序实现如下所示。

Example

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一。不过,我可以告诉您现在应该非常明显的缺点。看一下 else if 语句startElement方法。由于我们有标签<title>, link and description我们必须在当前的 XML 结构中进行跟踪。也就是说如果我们遇到一个<item>我们设置的起始标签inItem标记为true确保我们将正确的数据映射到正确的对象并在endElement我们将该标志设置为的方法false如果我们遇到一个</item>标签。表示我们已经完成了该项目标签。

在这个例子中,管理起来非常容易,但是必须解析具有不同级别的重复标签的更复杂的结构就变得很棘手。在那里,您必须使用枚举来设置当前状态,并使用大量 switch/case 语句来检查您所在的位置,或者更优雅的解决方案是某种使用标签堆栈的标签跟踪器。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使用 SAX 解析器解析 XML 的相关文章

  • 从手机访问本地主机[关闭]

    这个问题不太可能对任何未来的访客有帮助 它只与一个较小的地理区域 一个特定的时间点或一个非常狭窄的情况相关 通常不适用于全世界的互联网受众 为了帮助使这个问题更广泛地适用 访问帮助中心 help reopen questions 我正在使用
  • 是否可以通过编程方式查找 logback 日志文件?

    自动附加日志文件以支持电子邮件会很有用 我可以以编程方式设置路径 如以编程方式设置 Logback Appender 路径 https stackoverflow com questions 3803184 setting logback
  • android 中camera.setParameters 失败

    我已将相机功能包含在我的应用程序中 我还在市场上推出了该应用程序 我从一位用户那里收到一条错误消息 称他在打开相机时遇到错误 我已经在 2 1 的设备上测试了该应用程序 我从用户那里得到的错误是使用 Nexus One 它主要运行 2 2
  • 如何避免 ArrayIndexOutOfBoundsException 或 IndexOutOfBoundsException? [复制]

    这个问题在这里已经有答案了 如果你的问题是我得到了java lang ArrayIndexOutOfBoundsException在我的代码中 我不明白为什么会发生这种情况 这意味着什么以及如何避免它 这应该是最全面的典范 https me
  • 了解joda时间PeriodFormatter

    我以为我明白了 但显然我不明白 你能帮我通过这些单元测试吗 Test public void second assertEquals 00 00 01 OurDateTimeFormatter format 1000 Test public
  • 如何在 Spring 属性中进行算术运算?

  • 检测 ListView(或 ScrollView)内的滚动位置

    我正在构建一个聊天室应用程序 其中每 X 秒就会轮询一次新事件 每次发生这种情况时 此代码都会使用新数据更新 RoomAdapter ArrayAdapter 的自定义子类 并将其滚动到底部 RoomAdapter adapter Room
  • 使用架构注册表对 avro 消息进行 Spring 云合约测试

    我正在查看 spring 文档和 spring github 我可以看到一些非常基本的内容examples https github com spring cloud samples spring cloud contract sample
  • HashMap 值需要不可变吗?

    我知道 HashMap 中的键需要是不可变的 或者至少确保它们的哈希码 hashCode 不会改变或与另一个具有不同状态的对象发生冲突 但是 HashMap中存储的值是否需要与上面相同 为什么或者为什么不 这个想法是能够改变值 例如在其上调
  • 如何在 Java 中创建接受多个值的单个注释

    我有一个名为 Retention RetentionPolicy SOURCE Target ElementType METHOD public interface JIRA The Key Bug number JIRA referenc
  • java库维护数据库结构

    我的应用程序一直在开发 所以偶尔 当版本升级时 需要创建 更改 删除一些表 修改一些数据等 通常需要执行一些sql代码 是否有一个 Java 库可用于使我的数据库结构保持最新 通过分析类似 db structure version 信息并执
  • 使用布尔值进行冒泡排序以确定数组是否已排序

    我有以下用于冒泡排序的代码 但它根本不排序 如果我删除布尔值那么它工作正常 我知道 由于我的 a 0 小于所有其他元素 因此没有执行交换 任何人都可以帮助我解决这个问题 package com sample public class Bub
  • JMenu 中的文本居中

    好吧 我一直在网上寻找有关此问题的帮助 但我尝试的任何方法似乎都不起作用 我想让所有菜单文本都集中在菜单按钮上 当我使用setHorizontalTextPosition JMenu CENTER 没有变化 事实上 无论我使用什么常量 菜单
  • Hamcrest Matchers - 断言列表类型

    问题 我目前正在尝试使用 Hamcrest Matchers 来断言返回的列表类型是特定类型 例如 假设我的服务调用返回以下列表 List
  • 如何重新启动死线程? [复制]

    这个问题在这里已经有答案了 有哪些不同的可能性可以带来死线程回到可运行状态 如果您查看线程生命周期图像 就会发现一旦线程终止 您就无法返回到新位置 So 没有办法将死线程恢复到可运行状态 相反 您应该创建一个新的 Thread 实例
  • 具有矢量可绘制的 ImageView 的 Resources$NotFoundException

    我遇到了崩溃 Resources NotFoundException用于在活动创建时绘制的矢量 21 日前崩溃 安卓工作室2 1 支持库24 0 0 Gradle插件2 1 0 目标SDK 23 最小SDK 15 buildTools版本
  • 如何以编程方式创建活动转换?

    我想以编程方式创建一个动画 以从触摸屏点启动具有缩放效果的活动 接下来我模拟缩放输入效果
  • putFragment() - 片段 x 当前不在 FragmentManager 中

    上面的标题被问了很多次 但答案似乎与FragmentStatePagerAdapter这与我的问题无关 我正在使用该方法putFragment Bundle String Fragment 直接地 The 安卓文档 http develop
  • 洪水填充优化:尝试使用队列

    我正在尝试创建一种填充方法 该方法采用用户指定的初始坐标 检查字符 然后根据需要更改它 这样做之后 它会检查相邻的方块并重复该过程 经过一番研究 我遇到了洪水填充算法并尝试了该算法 它可以工作 但无法满足我对 250 x 250 个字符的数
  • Spring表单ModelAttribute字段验证避免400 Bad Request错误

    我有一个ArticleFormModel包含正常发送的数据html form由 Spring 使用注入 ModelAttribute注释 即 RequestMapping value edit method RequestMethod PO

随机推荐

  • Javascript 获取 元素内的文本

    我需要获取元素内的文本 我只能获取该元素的类 而不能获取 ID span class fileName test png span 所以我需要一种方法来获得test png 但正如你所见 我只有元素的类 没有 ID 请注意 我们可能还有更多
  • jquery ui sortable('refresh') 不起作用!

    我正在使用一个可排序列表 可以通过 jquery 由另一个脚本添加和删除元素 并且遇到一个奇怪的问题 这些新生成的项目似乎是可拖动的 但它们实际上不会排序 除非我在页面加载时已经有了它们 我尝试过使用 sortable refresh 但似
  • 防止新的原型方法出现在对象中键的 for 循环中

    我有一个函数 我想让它可供所有对象使用 它的 mergeObject object 函数定义如下 Object prototype mergeObjects function object if typeof object object r
  • 日期序列化后 Laravel 7 中的时区错误

    我正在开发一个新的 Laravel 7 1 应用程序 不是升级 但似乎使用日期序列化会失去时区 配置 应用程序 php timezone gt Europe Zurich 修补匠的例子 gt gt gt Carbon Carbon pars
  • 使用Python实现并集查找

    所以这就是我想要做的 我有一个包含几个等价关系的列表 l 1 2 2 3 4 5 6 7 1 7 我想合并共享一个元素的集合 这是一个示例实现 def union lis lis set e for e in lis res while T
  • Jackson JSON反序列化:每行中的数组元素

    我在用Jackson https github com FasterXML jackson core并且想要漂亮地打印 JSON 以便数组中的每个元素都进入每一行 例如 foo bar blah 1 2 3 Setting Serializ
  • Kubernetes:如何正确设置 php-fpm 和 nginx 共享卷权限

    我是 kubernetes 的新手 目前我正在尝试在 kubernetes 上部署 laravel 应用程序 我设置了 1 个部署 yaml 其中包含 2 个容器 nginx 和 php fpm 和一个共享卷 这是完整的 yaml apiV
  • 使用 C# .net 挂载其他用户 hive

    我正在编写一个应用程序 它将为每个选定的用户写入一些注册表项 我想知道是否有正确的方法来安装另一个用户的配置单元以在其中写入 目前 我正在使用 REG LOAD 来安装每个配置单元 它很实用 但很混乱 任何想法 预先感谢您的回答 Cheer
  • 如何在 OCR 中区分斜线零和八 (0->8)

    我正在为我的 Android 应用程序使用 ML Kit for Firebase ReCalc 收据计算器 https play google com store apps details id info trekto receipts
  • 消息总线中的总线发现

    我正试图了解消息总线和国际奥委会 但我的脑子里充满了问题 这就是我想到的场景 三台电脑通过局域网连接 无法访问互联网 这三台计算机各自运行一个服务 并自动自我发现其他计算机 换句话说 它们各自在公共总线上发送消息 这表明了他们自己的身份 从
  • 如何使用 Activator.CreateInstance 创建一个在运行时 T 未知的 List

    我在用着Activator CreateInstance通过类型变量创建对象 在运行时未知 static dynamic CreateFoo Type t gt Activator CreateInstance t 显然 我还没有正确理解如
  • 在构建期间调用 setState() 或 markNeedsBuild()。该覆盖小部件无法标记为需要构建

    我尝试在 AbsorbPointer 特别是 GestureDetector onPanEnd 调用的 fling 函数的 SetState 内使用 showGeneralDialog 编写动画对话框脚本 我附加了简单的代码 我尝试用未来的
  • std::atoll 与 VC++

    我一直在使用std atoll from cstdlib将字符串转换为int64 t与海湾合作委员会 该功能似乎在 Windows 工具链上不可用 使用 Visual Studio Express 2010 最好的选择是什么 我也有兴趣转换
  • 如何从 apt-get 下载软件包而不安装它? [关闭]

    Closed 这个问题是无关 help closed questions 目前不接受答案 我有一台电脑 没有NIC http en wikipedia org wiki Network interface controller 我想通过 U
  • 如何简化这些冗余的 C++ 代码?

    现有两个类 一个是SrcField它返回具体类型值 另一个是联合DSTField 定义相应的数据类型 class SrcField public signed char GetInt8 unsigned char GetUInt8 shor
  • 使用 SQL 在有向图中查找循环

    已经有几个关于查找循环的问题 但我没有在 SQL 中找到解决方案 首选 MSSQL 这些表将是 Node NodeID INT 和 Edge EdgeID INT NodeID1 INT NodeID2 INT 在有向图中查找循环的性能良好
  • ggadjustedcurves survminer if (xi > xj) 1L else -1L 错误

    我正在尝试使用 survminer 通过 ggadjustedcurves 创建调整后的生存曲线 我的代码如下 adjustedcurve lt coxph Surv time DeathTxCensor deadORtx 1 strata
  • 修复协议 Ecto.Queryable 未实现错误

    我刚开始使用 Ecto 和 Elixir 并且遇到了一个无法解释的错误 我的代码看起来就像 Ecto 自述文件中的示例 这是我的 Ecto 模型和查询模块 defmodule Registration do use Ecto Model s
  • 警报对话框肯定按钮问题

    我在单击按钮时启动了警报对话框 它有用户名和密码 在警报对话框触发后 如果您按 确定 按钮 它会强制关闭并关闭应用程序 任何帮助将不胜感激 public class MainActivity extends Activity final C
  • 如何使用 SAX 解析器解析 XML

    我正在关注这个tutorial http www anddev org parsing xml from the net using the saxparser t353 html 它工作得很好 但我希望它返回一个包含所有字符串的数组 而不