web-dev-qa-db-ja.com

PythonでXMLをきれいに印刷する

Pythonでxmlをきれいに印刷するための最善の方法(あるいはさまざまな方法でさえ)は何ですか?

378
Hortitude
import xml.dom.minidom

dom = xml.dom.minidom.parse(xml_fname) # or xml.dom.minidom.parseString(xml_string)
pretty_xml_as_string = dom.toprettyxml()
343
Ben Noland

lxmlは最新で更新されていて、きれいな印刷機能を含んでいます

import lxml.etree as etree

x = etree.parse("filename")
print etree.tostring(x, pretty_print=True)

Lxmlチュートリアルをチェックしてください。 http://lxml.de/tutorial.html

144
1729

もう1つの解決策は、2.5以降Pythonに組み込まれているElementTreeライブラリで使用するために、 this indent関数 を借用することです。これは次のようになります。

from xml.etree import ElementTree

def indent(elem, level=0):
    i = "\n" + level*"  "
    j = "\n" + (level-1)*"  "
    if len(elem):
        if not elem.text or not elem.text.strip():
            elem.text = i + "  "
        if not elem.tail or not elem.tail.strip():
            elem.tail = i
        for subelem in elem:
            indent(subelem, level+1)
        if not elem.tail or not elem.tail.strip():
            elem.tail = j
    else:
        if level and (not elem.tail or not elem.tail.strip()):
            elem.tail = j
    return elem        

root = ElementTree.parse('/tmp/xmlfile').getroot()
indent(root)
ElementTree.dump(root)
99
ade

これは醜いテキストノードの問題を回避するための私の(ハッキー?)解決策です。

uglyXml = doc.toprettyxml(indent='  ')

text_re = re.compile('>\n\s+([^<>\s].*?)\n\s+</', re.DOTALL)    
prettyXml = text_re.sub('>\g<1></', uglyXml)

print prettyXml

上記のコードは次のようになります。

<?xml version="1.0" ?>
<issues>
  <issue>
    <id>1</id>
    <title>Add Visual Studio 2005 and 2008 solution files</title>
    <details>We need Visual Studio 2005/2008 project files for Windows.</details>
  </issue>
</issues>

これの代わりに:

<?xml version="1.0" ?>
<issues>
  <issue>
    <id>
      1
    </id>
    <title>
      Add Visual Studio 2005 and 2008 solution files
    </title>
    <details>
      We need Visual Studio 2005/2008 project files for Windows.
    </details>
  </issue>
</issues>

免責事項:おそらくいくつかの制限があります。

46
Nick Bolton

他の人が指摘したように、lxmlにはかなりのプリンタが組み込まれています。

ただし、デフォルトではCDATAセクションが通常のテキストに変更されるため、厄介な結果が生じる可能性があります。

これは入力ファイルを保存し、インデントだけを変更するPython関数です(strip_cdata=Falseに注意してください)。さらに、出力がデフォルトのASCIIの代わりにUTF-8をエンコーディングとして使用することを確認します(encoding='utf-8'に注意してください)。

from lxml import etree

def prettyPrintXml(xmlFilePathToPrettyPrint):
    assert xmlFilePathToPrettyPrint is not None
    parser = etree.XMLParser(resolve_entities=False, strip_cdata=False)
    document = etree.parse(xmlFilePathToPrettyPrint, parser)
    document.write(xmlFilePathToPrettyPrint, pretty_print=True, encoding='utf-8')

使用例

prettyPrintXml('some_folder/some_file.xml')
21
roskakori

BeautifulSoupは使いやすいprettify()メソッドを持っています。

字下げレベルごとに1つのスペースを字下げします。これはlxmlのpretty_printよりもはるかにうまく機能し、短くて甘いです。

from bs4 import BeautifulSoup

bs = BeautifulSoup(open(xml_file), 'xml')
print bs.prettify()
14
ChaimG

私は上記の "ade"の答えを編集しようとしましたが、私が最初にフィードバックを匿名で提供した後でStack Overflowが私に編集をさせてくれないでしょう。これはElementTreeをきれいに表示する機能のバグが少ないバージョンです。

def indent(elem, level=0, more_sibs=False):
    i = "\n"
    if level:
        i += (level-1) * '  '
    num_kids = len(elem)
    if num_kids:
        if not elem.text or not elem.text.strip():
            elem.text = i + "  "
            if level:
                elem.text += '  '
        count = 0
        for kid in elem:
            indent(kid, level+1, count < num_kids - 1)
            count += 1
        if not elem.tail or not elem.tail.strip():
            elem.tail = i
            if more_sibs:
                elem.tail += '  '
    else:
        if level and (not elem.tail or not elem.tail.strip()):
            elem.tail = i
            if more_sibs:
                elem.tail += '  '
11

もしあなたがxmllintを持っているなら、あなたはサブプロセスを生成してそれを使うことができます。 xmllint --format <file>は入力XMLを標準出力にきれいに印刷します。

このメソッドはpythonの外部のプログラムを使用しているため、ハックのようなものになっています。

def pretty_print_xml(xml):
    proc = subprocess.Popen(
        ['xmllint', '--format', '/dev/stdin'],
        stdin=subprocess.PIPE,
        stdout=subprocess.PIPE,
    )
    (output, error_output) = proc.communicate(xml);
    return output

print(pretty_print_xml(data))
10
Russell Silva

DOM実装を使用している場合は、それぞれ独自のプリティプリント組み込み形式があります。

# minidom
#
document.toprettyxml()

# 4DOM
#
xml.dom.ext.PrettyPrint(document, stream)

# pxdom (or other DOM Level 3 LS-compliant imp)
#
serializer.domConfig.setParameter('format-pretty-print', True)
serializer.writeToString(document)

独自のプリティプリンタなしで何か他のものを使用している場合、またはそれらのプリティプリンタが望んだ方法でそれを実行しない場合は、おそらく独自のシリアライザを作成またはサブクラス化する必要があります。

8
bobince

ミニドームのきれいなプリントに問題がありました。与えられたエンコーディングの外の文字でドキュメントをきれいに印刷しようとするときはいつでも私はUnicodeErrorを得るでしょう、例えば、私がドキュメントの中にβがあってdoc.toprettyxml(encoding='latin-1')を試みたとき。これに対する私の回避策は次のとおりです。

def toprettyxml(doc, encoding):
    """Return a pretty-printed XML document in a given encoding."""
    unistr = doc.toprettyxml().replace(u'<?xml version="1.0" ?>',
                          u'<?xml version="1.0" encoding="%s"?>' % encoding)
    return unistr.encode(encoding, 'xmlcharrefreplace')
6
giltay
from yattag import indent

pretty_string = indent(ugly_string)

次のように要求しない限り、テキストノード内にスペースや改行を追加しません。

indent(mystring, indent_text = True)

インデントの単位をどのようにし、改行をどのようにするかを指定できます。

pretty_xml_string = indent(
    ugly_xml_string,
    indentation = '    ',
    newline = '\r\n'
)

ドキュメントは http://www.yattag.org のホームページにあります。

5

私は既存のElementTreeを見て、それをインデントするためにtext/tailを使用する解決策を書きました。

def prettify(element, indent='  '):
    queue = [(0, element)]  # (level, element)
    while queue:
        level, element = queue.pop(0)
        children = [(level + 1, child) for child in list(element)]
        if children:
            element.text = '\n' + indent * (level+1)  # for child open
        if queue:
            element.tail = '\n' + indent * queue[0][0]  # for sibling open
        else:
            element.tail = '\n' + indent * (level-1)  # for parent close
        queue[0:0] = children  # prepend so children come before siblings
3
nacitar sevaht

python用のXMLプリティプリント は、この作業にはかなり適しています。 (適切に名前も付けられています。)

もう1つの方法は、 pyXML を使用することです。これには、 PrettyPrint関数 があります。

3
Daniel Lew

unparsepretty=Trueを使うと、よく使われる外部ライブラリ xmltodict を使うことができます。

xmltodict.unparse(
    xmltodict.parse(my_xml), full_document=False, pretty=True)

上部のfull_document=Falseに対して<?xml version="1.0" encoding="UTF-8"?>

2

vkbeautify モジュールを見てください。

それは私の同じ名前の私の非常に人気のあるjavascript/nodejsプラグインのPython版です。 XML、JSON、CSSのテキストをきれいに印刷/縮小できます。入力と出力は任意の組み合わせの文字列/ファイルにできます。非常にコンパクトで、依存関係はありません。

import vkbeautify as vkb

vkb.xml(text)                       
vkb.xml(text, 'path/to/dest/file')  
vkb.xml('path/to/src/file')        
vkb.xml('path/to/src/file', 'path/to/dest/file') 
2
vadimk

再解析したくない場合は、get_pprint()関数を使った xmlpp.pyライブラリ があります。 lxmlのElementTreeオブジェクトに再解析しなくても、私のユースケースではうまくかつスムーズに動作しました。

1
gaborous

私はこの問題を抱えており、このようにそれを解決しました:

def write_xml_file (self, file, xml_root_element, xml_declaration=False, pretty_print=False, encoding='unicode', indent='\t'):
    pretty_printed_xml = etree.tostring(xml_root_element, xml_declaration=xml_declaration, pretty_print=pretty_print, encoding=encoding)
    if pretty_print: pretty_printed_xml = pretty_printed_xml.replace('  ', indent)
    file.write(pretty_printed_xml)

私のコードでは、このメソッドは次のように呼ばれています。

try:
    with open(file_path, 'w') as file:
        file.write('<?xml version="1.0" encoding="utf-8" ?>')

        # create some xml content using etree ...

        xml_parser = XMLParser()
        xml_parser.write_xml_file(file, xml_root, xml_declaration=False, pretty_print=True, encoding='unicode', indent='\t')

except IOError:
    print("Error while writing in log file!")

これがうまくいくのは、etreeがデフォルトでtwo spacesをインデントに使っているからです。インデントを強調することはあまりないので、きれいではありません。 etreeの設定や、標準etreeのインデントを変更するための関数のパラメータを設定できませんでした。私はそれがetreeを使用するのがいかに簡単かが好きですが、これは本当に私を悩ませました。

0
Zelphir
from lxml import etree
import xml.dom.minidom as mmd

xml_root = etree.parse(xml_fiel_path, etree.XMLParser())

def print_xml(xml_root):
    plain_xml = etree.tostring(xml_root).decode('utf-8')
    urgly_xml = ''.join(plain_xml .split())
    good_xml = mmd.parseString(urgly_xml)
    print(good_xml.toprettyxml(indent='    ',))

中国語のxmlでうまく機能しています!

0
Reed_Xia

xmlドキュメント全体をきれいなxmlドキュメントに変換するために
(例:LibreOffice Writerの.odtまたは.odsファイルを抽出(解凍)し、ugい "content.xml"ファイルを自動化されたgitバージョン管理およびgit difftooling of .odt/.ods files、たとえば here

import xml.dom.minidom

file = open("./content.xml", 'r')
xml_string = file.read()
file.close()

parsed_xml = xml.dom.minidom.parseString(xml_string)
pretty_xml_as_string = parsed_xml.toprettyxml()

file = open("./content_new.xml", 'w')
file.write(pretty_xml_as_string)
file.close()

参照:
- このページのBen Nolandの回答 に感謝します。

0
Gabriel Staples