2010-08-29 36 views
8

Tôi bị kẹt với XML và Python. Nhiệm vụ rất đơn giản nhưng tôi không thể giải quyết nó cho đến nay và chi tiêu trong thời gian dài đó. Tôi đến đây để được tư vấn cách giải quyết nó bằng vài dòng.Cách xóa các phần tử khỏi XML bằng cách sử dụng Python

Cảm ơn bạn đã trợ giúp với việc vượt qua cây. Tôi luôn luôn kết thúc với quá nhiều hoặc quá ít yếu tố. Các yếu tố có thể được lồng nhau mà không có giới hạn. Ví dụ được đưa ra chỉ là một ví dụ. Tôi sẽ chấp nhận bất kỳ giải pháp, không kén chọn về dom, minidom, sax, bất cứ điều gì ..

Tôi có một tập tin XML tương tự như thế này:

<root> 
    <elm> 
     <elm>Common content</elm> 

     <elm xmlns="http://example.org/ns"> 
      <elm lang="en">Content EN</elm> 
      <elm lang="cs">žluťoučký koníček</elm> 
     </elm> 

     <elm xml:id="abc123">Common content</elm> 

     <elm lang="en">Content EN</elm> 
     <elm lang="cs">Content CS</elm> 

     <elm lang="en"> 
      <elm>Content EN</elm> 
      <elm>Content EN</elm> 
     </elm> 

     <elm lang="cs"> 
      <elm>Content CS</elm> 
      <elm>Content CS</elm> 
     </elm> 
    </elm> 
</root> 

Những gì tôi cần - phân tích cú pháp XML và viết mới tập tin. Tệp mới phải chứa tất cả các phần tử cho ngôn ngữ và phần tử đã cho mà không có thuộc tính lang.

Đối với ngôn ngữ "cs" các tập tin đầu ra nên containt này:

<root> 
    <elm> 
     <elm>Common content</elm> 

     <elm xmlns="http://example.org/ns"> 
      <elm lang="cs">žluťoučký koníček</elm> 
     </elm> 

     <elm xml:id="abc123">Common content</elm> 

     <elm lang="cs">Content CS</elm> 

     <elm lang="cs"> 
      <elm>Content CS</elm> 
      <elm>Content CS</elm> 
     </elm> 
    </elm> 
</root> 

Nếu bạn có thể làm cho nó để bỏ qua các thuộc tính lang trong file mới, thậm chí tốt hơn. Nhưng điều đó không quan trọng.

UPDATE1: Đã thêm các ký tự unicode và thuộc tính không gian tên.

CẬP NHẬT 2: Sử dụng Python 2.5, thư viện chuẩn được ưu tiên.

+0

'Đối với ngôn ngữ" en ", tệp đầu ra phải làm rõ điều này:' Tôi giả sử bạn có ý nói rằng đầu ra đã cho là cho ngôn ngữ "cs"? – LarsH

+0

@LarsH: Tôi đã cập nhật câu hỏi để thêm một số ký tự unicode tại đó. Bạn nói đúng, cần phải viết: cho ngôn ngữ "cs". Sẽ thay đổi nó. – dwich

Trả lời

10

Sử dụng lxml:

import lxml.etree as le 

with open('doc.xml','r') as f: 
    doc=le.parse(f) 
    for elem in doc.xpath('//*[attribute::lang]'): 
     if elem.attrib['lang']=='en': 
      elem.attrib.pop('lang') 
     else: 
      parent=elem.getparent() 
      parent.remove(elem) 
    print(le.tostring(doc)) 

sản lượng

<root> 
    <elm>Common content</elm> 

    <elm> 
     <elm>Content EN</elm> 
     </elm> 

    <elm>Common content</elm> 

    <elm>Content EN</elm> 
    <elm> 
     <elm>Content EN</elm> 
     <elm>Content EN</elm> 
    </elm> 

    </root> 
+0

Cảm ơn rất nhiều. Không thể cài đặt lxml trên WinXP của tôi, vấn đề với trình biên dịch. Sẽ thử lại sau. – dwich

+0

Hoạt động! Cảm ơn! Bạn đã cứu đêm của tôi :) Tôi cảm ơn cả hai bạn, cả hai giải pháp đều tốt. – dwich

+0

Vui vì tôi có thể giúp :) – unutbu

5

Tôi không chắc cách tốt nhất để xóa thuộc tính lang, nhưng đây là một số mã thực hiện các thay đổi khác (Python 2.7; cho 2.5 hoặc 2.6, sử dụng getIterator thay vì iter), giả sử khi bạn xóa phần tử cũng luôn muốn xóa mọi thứ có trong phần tử đó.

Mã này chỉ cần in kết quả đầu ra tiêu chuẩn (bạn có thể chuyển hướng nó như bạn muốn, tất nhiên, hoặc trực tiếp ghi nó vào một số tập tin mới, và vân vân):

import sys 
from xml.etree import cElementTree as et 

def picklang(path, lang='en'): 
    tr = et.parse(path) 
    for element in tr.iter(): 
     for subelement in element: 
      la = subelement.get('lang') 
      if la is not None and la != lang: 
       element.remove(subelement) 
    return tr 

if __name__ == '__main__': 
    tr = picklang('la.xml') 
    tr.write(sys.stdout) 
    print 

Với la.xml là của bạn Ví dụ, đây viết

<root> 
    <elm>Common content</elm> 

    <elm> 
     <elm lang="en">Content EN</elm> 
     </elm> 

    <elm>Common content</elm> 

    <elm lang="en">Content EN</elm> 
    <elm lang="en"> 
     <elm>Content EN</elm> 
     <elm>Content EN</elm> 
    </elm> 

    </root> 
+0

Cảm ơn Alex, công trình tuyệt vời. Ngoại trừ hai thứ - không gian tên và unicode. Nếu có thuộc tính xmlns, ví dụ: '', nút mới sẽ nhận 'xmlns: ns0 =" http://example.org/ns "' thuộc tính và tất cả các nút con có một tiền tố ' dwich

+0

@dwich, để viết, bạn chỉ có thể thêm vào 'write' gọi một tham số' encoding' mà bạn chọn. Tính thẩm mỹ như vấn đề không gian tên (mà tôi tin rằng không thay đổi ngữ nghĩa của XML) là rất nhiều để giải quyết, than ôi (giống như, ví dụ, bạn có thể nhận thấy, sự thụt lề trong đầu ra là khác nhau, bởi vì khoảng trống trong các yếu tố bị xóa cũng biến mất). –

+0

Đó là điều unicode là sai lầm của tôi, tôi bắt đầu chơi với codec và mặc dù tôi đã sử dụng 'encoding = 'utf-8'', nó không hoạt động (coz mở nó không chính xác). Cảm ơn bạn cho câu trả lời của bạn, tôi sẽ chọn ~ unutbu's giải pháp như mã của ông không có vấn đề với điều không gian tên. Cả hai câu trả lời là chính xác. Cảm ơn các bạn! – dwich

1

cập nhật mã @ Alex Martelli để loại bỏ một lỗi ở đâu trong danh sách phần tử được cập nhật tại chỗ. Giải pháp trên sẽ trả lời sai nếu đầu vào phức tạp hơn một chút.

import sys 
from xml.etree import cElementTree as et 

def picklang(path, lang='en'): 
    tr = et.parse(path) 
    for element in tr.iter(): 
     for subelement in element[:]: 
      la = subelement.get('lang') 

      if la is not None and la != lang: 
       element.remove(subelement) 
    return tr 

if __name__ == '__main__': 
    tr = picklang('la.xml') 
    tr.write(sys.stdout) 
    print 

Mã trong dòng 7 for subelement in element: được thay đổi để for subelement in element[:]: vì nó là không chính xác để cập nhật danh sách tại chỗ trong khi iterating trên nó.

Mã này lặp qua bản sao danh sách phần tử và xóa các phần tử khi lang! = "Vi" trong danh sách phần tử gốc.

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