我的任务是解析Plone ZODB的巨大备份。 没有其他方法可以获得备份,但是在一个大约433mb的XML文件中。
请不要问为什么或如何,我只是解析文件的任务,以便检索图片,文件和其他重要数据。
我已经用Java编写了一个基于StAX的XML解析器,它现在可以读取文件,存储信息并在必要时将其打印到txt文件中。
现在我遇到的问题是:我需要检索的数据在哪里。 至于我可以读取XML文件(即使使用16GB内存也很难),它的节点都是相同的,只有属性与另一个不同(即记录节点中的“id”和“aka”其中有超过40000])。
是否有任何Plone或ZODB Dev可以帮助并指出我在这样的XML文件中存储数据的方式和位置的方向?我需要将哪种数据提供给解析器才能查找,存储和打印信息。
或者对于如何从XML文件中检索数据还有其他想法吗?
请记住,我>>不能<<除了Plone.xml
之外,还可以使用其他任何内容。
出于隐私和安全的明显原因,我也无法共享该文件。
答案 0 :(得分:8)
XML格式表示ZODB对象条目。
ZODB使用pickle
module作为将对象序列化为字节序列的基础。 XML文件格式试图为Python原始类型(数字,字符串,容器)提供单独的XML标记,但是您仍然可以获得“原始”对象数据,这些数据可能包含许多可能不是那么有趣的条目。你的任务。
在ZODB中,存储整个对象树;包含其他对象的对象包含更多。为了防止需要完全重写存储数据的此树中的任何更改,对象可以从专用持久性类继承,该类仅跟踪对该对象的更改,然后记录使用对这些单独记录的引用。
然后,XML格式在顶层包含<record>
个元素;这些表示在树中具有属性的单独对象,如果这些对象包含其他持久对象,则它们之间的引用被编码为<persistent>
个元素;看起来像:
<persistent>
<tuple>
<string id="[persistentid.subid]" encoding="base64">[base64-encoded-persistentid]</string>
<global id="[persistentid.subid]" name="[classname]" module="[module for class]"/>
</tuple>
</persistent>
然后,它表示具有两个值的Python元组; base64编码的持久ID (记录引用)和Python对象引用;后者可以被忽略,因为相同的信息在引用的<record>
元素中编码。
持久性ID 值是指另一条记录;取消引用这些内容的最简单方法是将其与aka
标记的<record>
属性进行匹配:
<record id="[persistentid]" aka="[base64-encoded-persistentid]">
持久性ID 实际上是无符号长整数的8字节大端表示; id
属性表示相同的数字:
>>> import struct
>>> 'AAAAAAAAAGU='.decode('base64')
'\x00\x00\x00\x00\x00\x00\x00e'
>>> struct.unpack('>Q', 'AAAAAAAAAGU='.decode('base64'))
(101,)
每个<record>
代码都包含1个或2个<pickle>
代码;第一个编码对象类型,第二个(如果存在)编码对象的状态。没有第二条记录,对象就是空的:
<record id="[persistentid]" aka="[base64-encoded-persistentid]">
<pickle>
<global id="[persistentid].1" name="[classname]" module="[module for class]"/>
</pickle>
<pickle>
<!-- ... -->
</pickle>
</record>
状态使用的类型取决于pickle对象的特定类;默认是获取类__dict__
并对其进行编码,但特定实现可以选择实现自定义__getstate__
method(以及相应的__setstate__
)。例如,对于BTrees
包,您通常会找到键值对和 Bucket
对象,这些对象只是将较大的btree拆分为单独的记录。
任何未从特殊持久性类继承的类的实例(因此不会获得单独的记录)都存储为<object>
标记,其中Python类记录为<klass>
标记,其次通过元组获取初始对象参数,以及可选状态。
如果您正在寻找大型二进制内容(图像,文件),可能运气不佳,因为所有现代Plone版本都使用ZODB BLOB支持,此类数据存储在单独的文件中。 XML文件只会指向空持久性记录,然后通过其他方式找到ZODB blob内容:
<record id="11545" aka="AAAAAAAALRk=">
<pickle>
<global id="11545.1" name="Blob" module="ZODB.blob"/>
</pickle>
<pickle>
<none/>
</pickle>
</record>
<none/>
标记表示Python None
对象(相当于Java中的null
)。然后blob数据不包含在导出中。
其他随机笔记:
<reference>
标记表示对先前已编码的对象的引用,但不包含具有单独的持久性<record>
的对象;这些指向[persistentid.subid]
值。毕竟不止一次记录同一个对象几乎没有意义。
<unicode>
标记值使用UTF-8编码; <永远不会设置encoding
属性。
DateTime.DateTime
模块已经注册了一个用于处理扩展类型的内部copy_reg
module function的包装器;您可能会找到以下条目:
<object id="5406.12">
<klass>
<global id="5406.9" name="_dt_reconstructor" module="DateTime.DateTime"/>
</klass>
<tuple>
<global id="5406.10" name="Splitter" module="Products.CMFPlone.UnicodeSplitter.splitter"/>
<global id="5406.11" name="object" module="__builtin__"/>
<none/>
</tuple>
</object>
此处_dt_reconstructor
用于创建Products.CMFPlone.UnicodeSplitter.splitter.Splitter
的新副本;它没有其他状态(没有<state>
标签)。