繁体   English   中英

在使用SAX进行解析时,如何保留未绑定到对象的XML节点

[英]How to preserve XML nodes that are not bound to an object when using SAX for parsing

我正在开发一个与蓝牙相机接口的Android应用程序。 对于存储在相机上的每个剪辑,我们在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类型的ArrayList(也许是DOM),并且在startTag中我检查该元素是否不是预定义标签之一,如果是,直到我到达该标签的末尾我存储整个结构(但在什么?)..然后在写回来时,我将浏览所有其他标签并将它们写出到xml文件(以及我当然知道的字段)

这是一个众所周知的解决方案的常见问题吗?

- 更新5/22/12 -

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

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

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

你说如果你想保留你没有“消耗”的节点,SAX可能不是最好的选择。 您仍然可以使用某种“sax存储”来保存SAX事件并重放它们(这些事情有一些实现),但基于对象模型的API将更容易使用:你' d轻松保留完整的对象模型,只需更新“您的”节点。

当然,你可以使用DOM这是标准 ,但你也可能要考虑其提供给您将使用在任意的数据模型的特定节点的更容易获得的替代品。 其中,JDOM( http://www.jdom.org/ )和XOM( http://www.xom.nu/ )是有趣的候选人。

以下是使用SAX过滤器的方法

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

我花了一些时间来完成这个想法并且它有效。 它基本上归结为XMLFilter的正确链接。 这是单元测试的样子,你的代码会做类似的事情:

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事件记录器包含在SAX解析器中
  • 你的Clip解析器( ClipHolder )缠绕在录音机上
  • 解析XML时,记录器将记录所有内容,而ClipHolder只会查看它所知道的内容
  • 然后,您可以clip对象执行任何操作
  • 然后将序列化器包裹在记录器周围(基本上将其重新映射到自身)
  • 然后,您将使用序列化程序,它将处理记录的事件(委托给父项并将self注册为ContentHandler ),并将其与clip对象的内容重叠。

在github上找到DVR代码和Clip测试。 我希望它有所帮助。

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

如果您没有绑定到特定的xml架构,则应考虑执行以下操作:

<data>
    <element id="title">
        myTitle
    </element>
    <element id="date">
         18/05/2012
    </element>
    ...
</data>

然后将所有这些元素存储在单个ArrayList中。 通过这种方式你不会丢失信息,你仍然有可能选择你想要显示的元素 - 编辑等...

你在XPath上比SAX解析慢20倍的假设是有缺陷的...... SAX解析只是一个低级的tokenizer,你的处理逻辑就会在其上构建......你的处理逻辑需要额外的解析...... XPath的性能有很多和实现一样...据我所知,vtd-xml的XPath至少比DOM快一个数量级,并且更适合于重型XML处理...下面是一些链接到进一步参考......

http://sdiwc.us/digitlib/journal_paper.php?paper=00000582.pdf

Android - XPath评估速度很慢

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM