草庐IT

java - 使用 SAX 进行解析时如何保留未绑定(bind)到对象的 XML 节点

coder 2023-12-05 原文

我正在开发一个与蓝牙相机接口(interface)的安卓应用程序。对于存储在相机上的每个剪辑,我们在 XML 文件中存储有关剪辑的一些字段(其中一些字段用户可以更改)。

目前这个应用程序是唯一将此 xml 数据写入设备的应用程序,但将来桌面应用程序或 iphone 应用程序也可能会在此处写入数据。我不想假设另一个应用程序也不能有额外的字段(特别是如果他们有一个更新版本的应用程序添加了这个版本尚不支持的新字段)。

所以我想防止的情况是,我们在另一个应用程序中向这个 XML 文件添加新字段,然后用户去使用 android 应用程序,它会删除​​那些其他字段,因为它不知道它们.

让我们举个假设的例子:

<data>
  <title>My Title</title>
  <date>12/24/2012</date>
  <category>Blah</category>
</data>

当从设备读取时,这将被翻译成一个看起来像这样的 Clip 对象 (为简洁起见进行了简化)

public class Clip {
  public String title, category;
  public Date date;
}

所以我使用 SAX 来解析数据并将其存储到 Clip 中。 我只是将字符存储在 StringBuilder 中,并在到达标题、类别和日期的结束元素时将它们写出。

我意识到,当我将这些数据写回设备时,如果原始文档中有任何其他标签,它们将不会被写入,因为我只写出我知道的字段。

这让我觉得 SAX 可能是错误的选择,也许我应该使用 DOM 或其他我可以更轻松地写出最初存在的任何其他元素的东西。

或者我在想,也许我的 Clip 类包含一些通用 XML 类型(可能是 DOM)的 ArrayList,并且在 startTag 中我检查该元素是否不是预定义标签之一,如果是,直到我到达结尾那个标签我存储了整个结构(但是在什么?)..然后写回我会遍历所有额外的标签并将它们写到 xml 文件中(当然还有我知道的字段)

这是已知解决方案的常见问题吗?

-- 2012 年 5 月 22 日更新 --

我没有提到在实际的 xml 根节点(实际上称为注释)中,我们使用已设置为 1 的版本号。短期内我要做的是要求版本我的应用程序支持的数字是 >= xml 数据的版本号。如果 xml 的数量更大,我将尝试解析以进行回读,但会拒绝对模型进行任何保存。尽管关于如何执行此操作,但我仍然对任何类型的工作示例感兴趣。

顺便说一句,我想到了另一个应该很简单的解决方案。我想我可以使用 XPATH 来查找我知道的节点,并在数据更新时替换这些节点的内容。然而,我运行了一些基准测试,当它被解析到内存中时,解析 xml 的开销是荒谬的。仅仅没有进行任何查找的解析操作导致性能比 SAX 差 20 倍。使用 xpath 进行解析通常要慢 30-50 倍,考虑到我在 ListView 中解析这些,这真的很糟糕。 所以我的想法是让 SAX 将节点解析为剪辑,但将整个 XML 存储在 Clip 类的一个变量中(记住,这个 xml 很短,不到 2kb)。然后,当我写回数据时,我可以使用 XPATH 替换我在原始 XML 中知道的节点。

但仍然对任何其他解决方案感兴趣。我可能不会接受解决方案,除非它包含一些代码示例。

最佳答案

以下是使用 SAX filters 的方法:

  1. 当您使用 SAX 阅读文档时,您会记录所有事件。您记录它们并将它们进一步冒泡到 SAX 阅读器的下一个级别。您基本上将两层 SAX 读取器(使用 XMLFilter)堆叠在一起 - 一层将记录和中继,另一层是您当前创建对象的 SAX 处理程序。
  2. 当您准备好将您的修改写回磁盘时,您会启动与您的编写器分层的记录的 SAX 事件,这些事件将覆盖您已更改的那些值/节点。

我花了一些时间研究这个想法,它奏效了。它基本上归结为 XMLFilter 的正确链接。方法如下 the unit test看起来,您的代码会做类似的事情:

final SAXParserFactory factory = SAXParserFactory.newInstance();
final SAXParser parser = factory.newSAXParser();

final RecorderProxy recorder = new RecorderProxy(parser.getXMLReader());
final ClipHolder clipHolder = new ClipHolder(recorder);

clipHolder.parse(new InputSource(new StringReader(srcXml)));

assertTrue(recorder.hasRecordingToReplay());

final Clip clip = clipHolder.getClip();
assertNotNull(clip);
assertEquals(clip.title, "My Title");
assertEquals(clip.category, "Blah!");
assertEquals(clip.date, Clip.DATE_FORMAT.parse("12/24/2012"));

clip.title = "My Title Updated";
clip.category = "Something else";

final ClipSerializer serializer = new ClipSerializer(recorder);
serializer.setClip(clip);

final TransformerFactory xsltFactory = TransformerFactory.newInstance();
final Transformer t = xsltFactory.newTransformer();
final StringWriter outXmlBuffer = new StringWriter();

t.transform(new SAXSource(serializer, 
            new InputSource()), new StreamResult(outXmlBuffer));

assertEquals(targetXml, outXmlBuffer.getBuffer().toString());

重要的几行是:

  • 你的 SAX events recorder包裹在 SAX 解析器中
  • 您的 Clip 解析器 ( ClipHolder ) 环绕在记录器上
  • 解析 XML 时,记录器将记录所有内容,而您的 ClipHolder 将只查看它知道的内容
  • 然后你可以用the clip object做任何你需要做的事
  • serializer然后缠绕在录音机上(基本上是将其重新映射到自身上)
  • 然后您使用序列化程序,它将负责提供记录的事件(委托(delegate)给父级并将 self 注册为 ContentHandler)并覆盖它所具有的内容说说 clip 对象。

请找到DVR代码和Clip测试 over at github 。希望对您有所帮助。

附注它不是一个通用的解决方案,整个记录->重播+覆盖概念在提供的实现中是非常基本的。基本上是一个插图。如果您的 XML 更复杂并且变得“多毛”(例如,不同级别上的相同元素名称等),则需要扩充逻辑。这个概念将保持不变。

关于java - 使用 SAX 进行解析时如何保留未绑定(bind)到对象的 XML 节点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10648651/

有关java - 使用 SAX 进行解析时如何保留未绑定(bind)到对象的 XML 节点的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. Ruby 解析字符串 - 2

    我有一个字符串input="maybe(thisis|thatwas)some((nice|ugly)(day|night)|(strange(weather|time)))"Ruby中解析该字符串的最佳方法是什么?我的意思是脚本应该能够像这样构建句子:maybethisissomeuglynightmaybethatwassomenicenightmaybethiswassomestrangetime等等,你明白了......我应该一个字符一个字符地读取字符串并构建一个带有堆栈的状态机来存储括号值以供以后计算,还是有更好的方法?也许为此目的准备了一个开箱即用的库?

  4. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  5. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  6. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  7. ruby - 在 Ruby 中使用匿名模块 - 2

    假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

  8. ruby - 使用 ruby​​ 和 savon 的 SOAP 服务 - 2

    我正在尝试使用ruby​​和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我

  9. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  10. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

随机推荐