kai*_*kai 13 python xml lxml namespaces
我正在解析由外部程序生成的xml文件.然后,我想使用自己的命名空间向此文件添加自定义注释.我的输入如下:
<sbml xmlns="http://www.sbml.org/sbml/level2/version4" xmlns:celldesigner="http://www.sbml.org/2001/ns/celldesigner" level="2" version="4">
<model metaid="untitled" id="untitled">
<annotation>...</annotation>
<listOfUnitDefinitions>...</listOfUnitDefinitions>
<listOfCompartments>...</listOfCompartments>
<listOfSpecies>
<species metaid="s1" id="s1" name="GenA" compartment="default" initialAmount="0">
<annotation>
<celldesigner:extension>...</celldesigner:extension>
</annotation>
</species>
<species metaid="s2" id="s2" name="s2" compartment="default" initialAmount="0">
<annotation>
<celldesigner:extension>...</celldesigner:extension>
</annotation>
</species>
</listOfSpecies>
<listOfReactions>...</listOfReactions>
</model>
</sbml>
Run Code Online (Sandbox Code Playgroud)
问题是lxml只在使用时声明名称空间,这意味着声明会重复多次,如此(简化):
<sbml xmlns="namespace" xmlns:celldesigner="morenamespace" level="2" version="4">
<listOfSpecies>
<species>
<kjw:test xmlns:kjw="http://this.is.some/custom_namespace"/>
<celldesigner:data>Some important data which must be kept</celldesigner:data>
</species>
<species>
<kjw:test xmlns:kjw="http://this.is.some/custom_namespace"/>
</species>
....
</listOfSpecies>
</sbml>
Run Code Online (Sandbox Code Playgroud)
是否可以强制lxml在父元素中只写一次此声明,例如sbml
或listOfSpecies
?或者有充分的理由不这样做吗?我想要的结果是:
<sbml xmlns="namespace" xmlns:celldesigner="morenamespace" level="2" version="4" xmlns:kjw="http://this.is.some/custom_namespace">
<listOfSpecies>
<species>
<kjw:test/>
<celldesigner:data>Some important data which must be kept</celldesigner:data>
</species>
<species>
<kjw:test/>
</species>
....
</listOfSpecies>
</sbml>
Run Code Online (Sandbox Code Playgroud)
重要的问题是必须保留从文件中读取的现有数据,因此我不能只创建一个新的根元素(我想?).
编辑:下面的代码.
def annotateSbml(sbml_input):
from lxml import etree
checkSbml(sbml_input) # Makes sure the input is valid sbml/xml.
ns = "http://this.is.some/custom_namespace"
etree.register_namespace('kjw', ns)
sbml_doc = etree.ElementTree()
root = sbml_doc.parse(sbml_input, etree.XMLParser(remove_blank_text=True))
nsmap = root.nsmap
nsmap['sbml'] = nsmap[None] # Makes code more readable, but seems ugly. Any alternatives to this?
nsmap['kjw'] = ns
ns = '{' + ns + '}'
sbmlns = '{' + nsmap['sbml'] + '}'
for species in root.findall('sbml:model/sbml:listOfSpecies/sbml:species', nsmap):
species.append(etree.Element(ns + 'test'))
sbml_doc.write("test.sbml.xml", pretty_print=True, xml_declaration=True)
return
Run Code Online (Sandbox Code Playgroud)
在lxml中无法修改节点的命名空间映射.查看此功能作为心愿单项目的此打开的票证.
它起源于lxml邮件列表中的此线程,其中替换根节点的替代方法.更换根节点存在一些问题:请参阅上面的故障单.
为了完整起见,我将把建议的根替换解决方法代码放在这里:
>>> DOC = """<sbml xmlns="http://www.sbml.org/sbml/level2/version4" xmlns:celldesigner="http://www.sbml.org/2001/ns/celldesigner" level="2" version="4">
... <model metaid="untitled" id="untitled">
... <annotation>...</annotation>
... <listOfUnitDefinitions>...</listOfUnitDefinitions>
... <listOfCompartments>...</listOfCompartments>
... <listOfSpecies>
... <species metaid="s1" id="s1" name="GenA" compartment="default" initialAmount="0">
... <annotation>
... <celldesigner:extension>...</celldesigner:extension>
... </annotation>
... </species>
... <species metaid="s2" id="s2" name="s2" compartment="default" initialAmount="0">
... <annotation>
... <celldesigner:extension>...</celldesigner:extension>
... </annotation>
... </species>
... </listOfSpecies>
... <listOfReactions>...</listOfReactions>
... </model>
... </sbml>"""
>>>
>>> from lxml import etree
>>> from StringIO import StringIO
>>> NS = "http://this.is.some/custom_namespace"
>>> tree = etree.ElementTree(element=None, file=StringIO(DOC))
>>> root = tree.getroot()
>>> nsmap = root.nsmap
>>> nsmap['kjw'] = NS
>>> new_root = etree.Element(root.tag, nsmap=nsmap)
>>> new_root[:] = root[:]
>>> new_root.append(etree.Element('{%s}%s' % (NS, 'test')))
>>> new_root.append(etree.Element('{%s}%s' % (NS, 'test')))
>>> print etree.tostring(new_root, pretty_print=True)
<sbml xmlns:celldesigner="http://www.sbml.org/2001/ns/celldesigner" xmlns:kjw="http://this.is.some/custom_namespace" xmlns="http://www.sbml.org/sbml/level2/version4"><model metaid="untitled" id="untitled">
<annotation>...</annotation>
<listOfUnitDefinitions>...</listOfUnitDefinitions>
<listOfCompartments>...</listOfCompartments>
<listOfSpecies>
<species metaid="s1" id="s1" name="GenA" compartment="default" initialAmount="0">
<annotation>
<celldesigner:extension>...</celldesigner:extension>
</annotation>
</species>
<species metaid="s2" id="s2" name="s2" compartment="default" initialAmount="0">
<annotation>
<celldesigner:extension>...</celldesigner:extension>
</annotation>
</species>
</listOfSpecies>
<listOfReactions>...</listOfReactions>
</model>
<kjw:test/><kjw:test/></sbml>
Run Code Online (Sandbox Code Playgroud)
我知道这是个老问题,但它仍然有效,从 lxml 3.5.0 开始,这个问题可能有更好的解决方案:
cleanup_namespaces()
接受一个新参数top_nsmap
,将提供的前缀命名空间映射的定义移动到树的顶部。
所以现在命名空间映射可以通过简单的调用来向上移动:
nsmap = {'kjw': 'http://this.is.some/custom_namespace'}
etree.cleanup_namespaces(root, top_nsmap=nsmap)
Run Code Online (Sandbox Code Playgroud)