Подтвердить что ты не робот

Lxml: добавить пространство имен для ввода файла

Я разбираю 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>

Проблема заключается в том, что 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>

Можно ли заставить 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>

Важная проблема заключается в том, что существующие данные, которые считываются из файла, должны храниться, поэтому я не могу просто создать новый корневой элемент (я думаю?).

EDIT: приведенный ниже код.

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
4b9b3361

Ответ 1

Изменение отображения пространства имен в узле невозможно в 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> ... <listOfUnitDefinitions> ... </listOfUnitDefinitions> ... <listOfCompartments> ... </listOfCompartments> ... <listOfSpecies> ... < species metaid = "s1" id = "s1" name= "GenA" отделение = "default" initialAmount = "0" & ​​gt; ... < аннотация > ... < celldesigner: extension > ... </celldesigner: extension > ... </annotation> ... </species> ... < species metaid = "s2" id = "s2" name= "s2" отделение = "default" initialAmount = "0" & ​​gt; ... < аннотация > ... < celldesigner: extension > ... </celldesigner: extension > ... </annotation> ... </species> ... </listOfSpecies> ... <listOfReactions> ... </listOfReactions> ... </model> ... </sbml> "" → → → от lxml import etree → > из 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. msgstr "".   < & аннотации GT;... </аннотации >   <listOfUnitDefinitions> ... </listOfUnitDefinitions>   < & listOfCompartments GT;... </listOfCompartments>   <listOfSpecies>     < species metaid = "s1" id = "s1" name= "GenA" отделение = "default" initialAmount = "0" & ​​gt;       < & аннотации GT;         < celldesigner: расширение > ... </celldesigner: расширение >       </аннотации >     < &/вида GT;     < species metaid = "s2" id = "s2" name= "s2" отделение = "default" initialAmount = "0" & ​​gt;       < & аннотации GT;          < celldesigner: расширение > ... </celldesigner: расширение >       </аннотации >     < &/вида GT;   < &/listOfSpecies GT;   <listOfReactions> ... </listOfReactions> </модель > < KJW: испытание / > < KJW: испытание / > </SBML> Код >

Ответ 2

Вместо прямого обращения с необработанным XML вы также можете посмотреть LibSBML, библиотеку для управления документами SBML с привязками языка, среди прочего, питона. Там вы будете использовать его следующим образом:

>>> from libsbml import *
>>> doc = readSBML('Dropbox/SBML Models/BorisEJB.xml')
>>> species = doc.getModel().getSpecies('MAPK')
>>> species.appendAnnotation('<kjw:test xmlns:kjw="http://this.is.some/custom_namespace"/>')
0
>>> species.toSBML()
'<species id="MAPK" compartment="compartment" initialConcentration="280" boundaryCondition="false">\n  <annotation>\n
 <kjw:test xmlns:kjw="http://this.is.some/custom_namespace"/>\n  </annotation>\n</species>'
>>>

Ответ 3

Если вы временно добавите атрибут с именами в корневой каталог node, который выполняет трюк.

ns = '{http://this.is.some/custom_namespace}'

# add 'kjw:foobar' attribute to root node
root.set(ns+'foobar', 'foobar')

# add kjw namespace elements (or attributes) elsewhere
... get child element species ...
species.append(etree.Element(ns + 'test'))

# remove temporary namespaced attribute from root node
del root.attrib[ns+'foobar']

Ответ 4

Я знаю, что это старый вопрос, но он по-прежнему действителен, и, как и в случае с lxml 3.5.0, вероятно, лучшее решение этой проблемы:

cleanup_namespaces() принимает новый аргумент top_nsmap, который перемещает определения предоставленного префикса-пространства имен в верхнюю часть дерева.

Итак, теперь карту пространства имен можно перенести с помощью простого вызова:

nsmap = {'kjw': 'http://this.is.some/custom_namespace'}
etree.cleanup_namespaces(root, top_nsmap=nsmap)

Ответ 5

Вы можете заменить корневой элемент, чтобы добавить 'kjw' в свой nsmap. Тогда объявление xmlns будет только в корневом элементе.