2013-08-10 3 views
11

У меня есть xml-файл, который мне нужно открыть и внести некоторые изменения, одним из этих изменений является удаление пространства имен и префикса, а затем сохранение в другой файл. Вот XML:Удалить пространство имен и префикс из xml в python с помощью lxml

<?xml version='1.0' encoding='UTF-8'?> 
<package xmlns="http://apple.com/itunes/importer"> 
    <provider>some data</provider> 
    <language>en-GB</language> 
</package> 

я могу сделать другие изменения, мне нужно, но не могу найти, как удалить пространство имен и префикс. Это reusklt XML мне нужно:

<?xml version='1.0' encoding='UTF-8'?> 
<package> 
    <provider>some data</provider> 
    <language>en-GB</language> 
</package> 

И вот мой скрипт, который будет открывать и анализировать XML и сохранить его:

metadata = '/Users/user1/Desktop/Python/metadata.xml' 
from lxml import etree 
parser = etree.XMLParser(remove_blank_text=True) 
open(metadata) 
tree = etree.parse(metadata, parser) 
root = tree.getroot() 
tree.write('/Users/user1/Desktop/Python/done.xml', pretty_print = True, xml_declaration = True, encoding = 'UTF-8') 

Так как бы я добавить код в моем сценарии, которые будут удалены пространство имен и префикс?

ответ

22

Replace тег как предполагает Uku Loskit. В дополнение к этому, используйте lxml.objectify.deannotate.

from lxml import etree, objectify 

metadata = '/Users/user1/Desktop/Python/metadata.xml' 
parser = etree.XMLParser(remove_blank_text=True) 
tree = etree.parse(metadata, parser) 
root = tree.getroot() 

####  
for elem in root.getiterator(): 
    if not hasattr(elem.tag, 'find'): continue # (1) 
    i = elem.tag.find('}') 
    if i >= 0: 
     elem.tag = elem.tag[i+1:] 
objectify.deannotate(root, cleanup_namespaces=True) 
#### 

tree.write('/Users/user1/Desktop/Python/done.xml', 
      pretty_print=True, xml_declaration=True, encoding='UTF-8') 

UPDATE

Некоторые теги, как Comment возвращающую функцию при доступе к tag атрибут. добавил охранник для этого. (1)

+0

Поиск() на elem.tag не удастся, если это встроенная функция Комментарий. Вы хотите проверить строку с чем-то вроде следующего: if isinstance (elem.tag, basestring): do_something(). Это для 2.x. Используйте isinstance (elem.tag, str) в 3.x. –

+0

@JeffLoughridge, Спасибо за комментарий. Я соответствующим образом обновил ответ. – falsetru

4
import xml.etree.ElementTree as ET 
def remove_namespace(doc, namespace): 
    """Remove namespace in the passed document in place.""" 
    ns = u'{%s}' % namespace 
    nsl = len(ns) 
    for elem in doc.getiterator(): 
     if elem.tag.startswith(ns): 
      elem.tag = elem.tag[nsl:] 

metadata = '/Users/user1/Desktop/Python/metadata.xml' 
tree = ET.parse(metadata) 
root = tree.getroot() 

remove_namespace(root, u'http://apple.com/itunes/importer') 
tree.write('/Users/user1/Desktop/Python/done.xml', 
     pretty_print=True, xml_declaration=True, encoding='UTF-8') 

Использован фрагмент кода из here Этот метод может быть легко расширена, чтобы удалить все пространства имен атрибутов с помощью функции поиска по тегам, которые начинаются с «Xmlns»

1

все, что вам нужно сделать, это:

objectify.deannotate(root, cleanup_namespaces=True) 

после того как вы получите корень, используя root = tree.getroot()

0

Вот два других способа удаления пространств имен. Первый использует помощник lxml.etree.QName, а второй использует регулярные выражения. Обе функции позволяют сопоставлять необязательный список пространств имен. Если список пространств имен не указан, все пространства имен удаляются. Клавиши атрибута также очищаются.

from lxml import etree 
import re 

def remove_namespaces_qname(doc, namespaces=None): 

    for el in doc.getiterator(): 

     # clean tag 
     q = etree.QName(el.tag) 
     if q is not None: 
      if namespaces is not None: 
       if q.namespace in namespaces: 
        el.tag = q.localname 
      else: 
       el.tag = q.localname 

      # clean attributes 
      for a, v in el.items(): 
       q = etree.QName(a) 
       if q is not None: 
        if namespaces is not None: 
         if q.namespace in namespaces: 
          del el.attrib[a] 
          el.attrib[q.localname] = v 
        else: 
         del el.attrib[a] 
         el.attrib[q.localname] = v 
    return doc 


def remove_namespace_re(doc, namespaces=None): 

    if namespaces is not None: 
     ns = list(map(lambda n: u'{%s}' % n, namespaces)) 

    for el in doc.getiterator(): 

     # clean tag 
     m = re.match(r'({.+})(.+)', el.tag) 
     if m is not None: 
      if namespaces is not None: 
       if m.group(1) in ns: 
        el.tag = m.group(2) 
      else: 
       el.tag = m.group(2) 

      # clean attributes 
      for a, v in el.items(): 
       m = re.match(r'({.+})(.+)', a) 
       if m is not None: 
        if namespaces is not None: 
         if m.group(1) in ns: 
          del el.attrib[a] 
          el.attrib[m.group(2)] = v 
        else: 
         del el.attrib[a] 
         el.attrib[m.group(2)] = v 
    return doc