簡體   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