2012-03-06 35 views
10

Tôi có một tài liệu trong đó sử dụng một không gian tên XML mà tôi muốn tăng /group/house/dogs bởi một: (các tập tin được gọi là houses.xml)Python: Cập nhật file XML sử dụng ElementTree trong khi bảo tồn bố trí càng nhiều càng tốt

<?xml version="1.0"?> 
<group xmlns="http://dogs.house.local"> 
    <house> 
      <id>2821</id> 
      <dogs>2</dogs> 
    </house> 
</group> 

kết quả hiện tại của tôi bằng cách sử dụng mã dưới đây là: (các tập tin được tạo ra được gọi là houses2.xml)

<ns0:group xmlns:ns0="http://dogs.house.local"> 
    <ns0:house> 
     <ns0:id>2821</ns0:id> 
     <ns0:dogs>3</ns0:dogs> 
    </ns0:house> 
</ns0:group> 

tôi muốn sửa chữa hai điều (nếu nó có thể sử dụng ElementTree Nếu nó không được, tôi muốn được. tuyệt vời cho một gợi ý về những gì tôi nên sử dụng thay vào đó) :

  1. Tôi muốn giữ dòng <?xml version="1.0"?>.
  2. Tôi không muốn thêm tiền tố vào tất cả các thẻ, tôi muốn giữ nguyên nó.

Kết luận, Tôi không muốn gây rối với tài liệu nhiều hơn tôi hoàn toàn phải làm.

Mã hiện tại của tôi (hoạt động ngoại trừ các lỗ hổng được đề cập ở trên) tạo ra kết quả ở trên theo sau.

Tôi đã thực hiện một chức năng tiện ích mà tải một file XML sử dụng ElementTree và trả về ElementTree và không gian tên (như tôi không muốn cứng mã namespace, và sẵn sàng chấp nhận rủi ro nó ngụ ý):

def elementTreeRootAndNamespace(xml_file): 
    from xml.etree import ElementTree 
    import re 
    element_tree = ElementTree.parse(xml_file) 

    # Search for a namespace on the root tag 
    namespace_search = re.search('^({\S+})', element_tree.getroot().tag) 
    # Keep the namespace empty if none exists, if a namespace exists set 
    # namespace to {namespacename} 
    namespace = '' 
    if namespace_search: 
     namespace = namespace_search.group(1) 

    return element_tree, namespace 

Đây là mã của tôi để cập nhật số lượng chó và lưu nó vào tập tin mới houses2.xml:

elementTree, namespace = elementTreeRootAndNamespace('houses.xml') 

# Insert the namespace before each tag when when finding current number of dogs, 
# as ElementTree requires the namespace to be prefixed within {...} when a 
# namespace is used in the document. 
dogs = elementTree.find('{ns}house/{ns}dogs'.format(ns = namespace)) 

# Increase the number of dogs by one 
dogs.text = str(int(dogs.text) + 1) 

# Write the result to the new file houses2.xml. 
elementTree.write('houses2.xml') 
+0

Tôi đoán đây là bản sao của http://stackoverflow.com/questions/8983041/saving-xml-files-using-elementtree và http://stackoverflow.com/questions/18338807/cannot-write-xml- không gian tên tập tin với mặc định – leo

Trả lời

2

Round-vấp ngã, không may, không phải là một vấn đề tầm thường. Với XML, thường không thể bảo toàn tài liệu gốc trừ khi bạn sử dụng một trình phân tích cú pháp đặc biệt (như DecentXML nhưng đó là dành cho Java).

Tùy thuộc vào nhu cầu của bạn, bạn có các tùy chọn sau:

  • Nếu bạn kiểm soát nguồn và bạn có thể bảo mật mã của bạn với các bài kiểm tra đơn vị, bạn có thể viết riêng, phân tích cú pháp đơn giản của bạn. Trình phân tích cú pháp này không chấp nhận XML nhưng chỉ có một tập con giới hạn. Bạn có thể, ví dụ, đọc toàn bộ tài liệu dưới dạng một chuỗi và sau đó sử dụng các hoạt động chuỗi của Python để định vị <dogs> và thay thế bất kỳ thứ gì lên đến < tiếp theo. Hack? Vâng.

  • Bạn có thể lọc đầu ra. XML chỉ cho phép chuỗi <ns0: chỉ ở một nơi, vì vậy bạn có thể tìm kiếm & thay thế bằng < và sau đó giống với <group xmlns:ns0="<group xmlns=". Điều này khá an toàn trừ khi bạn có thể có CDATA trong XML của mình.

  • Bạn có thể viết trình phân tích cú pháp XML của riêng bạn, đơn giản. Đọc đầu vào dưới dạng chuỗi và sau đó tạo phần tử cho mỗi cặp <> cộng với vị trí của chúng trong đầu vào. Điều đó cho phép bạn lấy đầu vào một cách nhanh chóng nhưng chỉ hoạt động đối với các đầu vào nhỏ.

+0

Cảm ơn. Có, nó sẽ mất nhiều thời gian hơn bạn nghĩ cho một cái gì đó đơn giản như XML ... ;-) –

+0

Vì tôi đã không tìm thấy trình phân tích cú pháp dựa trên XML nào có thể làm được điều này, tôi đánh dấu câu trả lời này là chính xác. Tôi đang tiếp tục sử dụng giải pháp mà bạn đã đề xuất ở mục danh sách đầu tiên (đã có trong mã tôi đang chuyển sang Python). Để có được những gì tôi muốn tôi sẽ nhiều hơn hoặc ít hơn phải viết một phân tích cú pháp tùy chỉnh, mà sẽ mất quá nhiều thời gian cho công việc tôi đang giải quyết (mặc dù nó sẽ được vui vẻ).Chỉ cần loại bỏ Deleted

3

Một giải pháp dựa trên XML cho vấn đề này là để viết một lớp helper cho ElementTree đó:

  • Grabs dòng XML-khai trước khi phân tích cú pháp như ElementTree tại thời điểm viết là không thể viết một dòng khai báo XML mà không cần viết một thuộc tính mã hóa (tôi đã kiểm tra nguồn).
  • Phân tích cú pháp tệp đầu vào một lần, lấy vùng tên của phần tử gốc. Đăng ký không gian tên với ElementTree như có chuỗi rỗng làm tiền tố. Khi điều đó được thực hiện, tệp nguồn được phân tích cú pháp bằng cách sử dụng ElementTree một lần nữa, với cài đặt mới đó.

Nó có một nhược điểm lớn:

  • XML comments bị mất. Điều tôi đã học được là không thể chấp nhận được cho tình huống này (ban đầu tôi không nghĩ rằng dữ liệu đầu vào có bất kỳ nhận xét nào, nhưng hóa ra nó có).

lớp helper của tôi với ví dụ:

from xml.etree import ElementTree as ET 
import re 


class ElementTreeHelper(): 
    def __init__(self, xml_file_name): 
     xml_file = open(xml_file_name, "rb") 

     self.__parse_xml_declaration(xml_file) 

     self.element_tree = ET.parse(xml_file) 
     xml_file.seek(0) 

     root_tag_namespace = self.__root_tag_namespace(self.element_tree) 
     self.namespace = None 
     if root_tag_namespace is not None: 
      self.namespace = '{' + root_tag_namespace + '}' 
      # Register the root tag namespace as having an empty prefix, as 
      # this has to be done before parsing xml_file we re-parse. 
      ET.register_namespace('', root_tag_namespace) 
      self.element_tree = ET.parse(xml_file) 

    def find(self, xpath_query): 
     return self.element_tree.find(xpath_query) 

    def write(self, xml_file_name): 
     xml_file = open(xml_file_name, "wb") 
     if self.xml_declaration_line is not None: 
      xml_file.write(self.xml_declaration_line + '\n') 

     return self.element_tree.write(xml_file) 

    def __parse_xml_declaration(self, xml_file): 
     first_line = xml_file.readline().strip() 
     if first_line.startswith('<?xml') and first_line.endswith('?>'): 
      self.xml_declaration_line = first_line 
     else: 
      self.xml_declaration_line = None 
     xml_file.seek(0) 

    def __root_tag_namespace(self, element_tree): 
     namespace_search = re.search('^{(\S+)}', element_tree.getroot().tag) 
     if namespace_search is not None: 
      return namespace_search.group(1) 
     else: 
      return None 


def __main(): 
    el_tree_hlp = ElementTreeHelper('houses.xml') 

    dogs_tag = el_tree_hlp.element_tree.getroot().find(
        '{ns}house/{ns}dogs'.format(
         ns=el_tree_hlp.namespace)) 
    one_dog_added = int(dogs_tag.text.strip()) + 1 
    dogs_tag.text = str(one_dog_added) 

    el_tree_hlp.write('hejsan.xml') 

if __name__ == '__main__': 
    __main() 

Sản lượng:

<?xml version="1.0"?> 
<group xmlns="http://dogs.house.local"> 
    <house> 
      <id>2821</id> 
      <dogs>3</dogs> 
    </house> 
</group> 

Nếu ai đó có một sự cải tiến để giải pháp này xin vui lòng đừng ngần ngại để lấy mã và cải thiện nó.

+0

Tôi sẽ đợi một tuần hoặc lâu hơn trong trường hợp người khác (hoặc tôi) có một cải tiến hoặc một giải pháp tốt hơn tất cả cùng nhau. – Deleted

+0

Bạn đã thử thêm không gian tên với tiền tố trống chưa? hoặc bạn có nhiều không gian tên và bạn không thể nói trước một không gian tên nào? –

+1

Tôi có nhiều không gian tên. Tôi biết nó có nguy cơ đối phó với họ theo cùng một cách, nhưng đối với dữ liệu của tôi, tôi nghĩ rằng nó đủ an toàn. Đó là lý do tại sao tôi phân tích cú pháp tệp một lần, lấy lại không gian tên và phân tích cú pháp. Tôi có thể phân tích cú pháp không gian tên của mình theo tệp io, sau đó là thefile.seek (0) và để cho ElementTree thực hiện điều đó trên tệp. Nhưng tôi muốn cùng một logic để phân tích phần không gian tên cả hai lần. – Deleted

1

khi Lưu xml thêm lập luận default_namespace rất dễ dàng để tránh ns0, trên mã của tôi

mã chủ chốt: xmltree.write (xmlfiile, "utf-8", default_namespace = xmlnamespace)

if os.path.isfile(xmlfiile): 
      xmltree = ET.parse(xmlfiile) 
      root = xmltree.getroot() 
      xmlnamespace = root.tag.split('{')[1].split('}')[0] //get namespace 

      initwin=xmltree.find("./{"+ xmlnamespace +"}test") 
      initwin.find("./{"+ xmlnamespace +"}content").text = "aaa" 
      xmltree.write(xmlfiile,"utf-8",default_namespace=xmlnamespace) 
0

etree từ lxml cung cấp tính năng này.

  1. elementTree.write('houses2.xml',encoding = "UTF-8",xml_declaration = True) giúp bạn trong việc không để lọt tờ khai

  2. Trong khi viết vào tập tin đó không làm thay đổi không gian tên.

http://lxml.de/parsing.html là liên kết để được hướng dẫn.

P.S: lxml nên được cài đặt riêng.

Các vấn đề liên quan