2017-12-22 2 views
0

属性値に基づいて並べ替えるXMLファイルがあります。このコードは、ちょうど箱の属性を再調整していないPythonで各親ノードのすべての子ノードを保持する属性に基づいてXMLを並べ替え

import xml.etree.ElementTree as ET 
tree = ET.parse("finalxml.xml") 
container = tree.find("images") 
data = [] 
for elem in container: 
    key = elem.findtext("image") 
    data.append((key,elem)) 
data.sort() 
container[:] = [item[-1] for item in data] 
tree.write("new-data.xml") 

:私は次の2つのオプションを試してみました

<?xml-stylesheet type='text/xsl' href='image_metadata_stylesheet.xsl'?> 
<dataset> 
    <name>imglab dataset</name> 
    <comment>Created by imglab tool.</comment> 
    <images> 
    <image file="/home/iris/Documents/SONY_MAX-20150408-200026-210358-00001.jpg"/> 
    <image file="/home/iris/Documents/SONY_MAX-20150408-200026-210358-00002.jpg"> 
     <box top="198" left="17" width="32" height="10"> 
     <label>sightscreen_pepsi</label> 
     </box> 
    </image> 
    <image file="/home/iris/Documents/SONY_MAX-20150408-200026-210358-00003.jpg"> 
     <box top="175" left="59" width="73" height="29"> 
     <label>groundpainting_hotstar</label> 
     </box> 
     <box top="174" left="205" width="56" height="24"> 
     <label>groundpainting_yesbank</label> 
     </box> 
     <box top="170" left="141" width="44" height="32"> 
     <label>groundpainting_vodafone</label> 
     </box> 
    </image> 
</images> 
</dataset> 

:所望の出力がこれです

<?xml-stylesheet type='text/xsl' href='image_metadata_stylesheet.xsl'?> 
<dataset> 
    <name>imglab dataset</name> 
    <comment>Created by imglab tool.</comment> 
    <images> 
    <image file="/home/iris/Documents/SONY_MAX-20150408-200026-210358-00003.jpg"> 
     <box top="175" left="59" width="73" height="29"> 
     <label>groundpainting_hotstar</label> 
     </box> 
     <box top="174" left="205" width="56" height="24"> 
     <label>groundpainting_yesbank</label> 
     </box> 
     <box top="170" left="141" width="44" height="32"> 
     <label>groundpainting_vodafone</label> 
     </box> 
    </image> 
    <image file="/home/iris/Documents/SONY_MAX-20150408-200026-210358-00001.jpg"/> 
    <image file="/home/iris/Documents/SONY_MAX-20150408-200026-210358-00002.jpg"> 
     <box top="198" left="17" width="32" height="10"> 
     <label>sightscreen_pepsi</label> 
     </box> 
    </image> 
</images> 
</dataset> 

:以下は、XMLファイルですイメージファイルの属性です。これは望ましくありません。以下は私がSOから取ったものですが、何もしません。

# ======================================================================= 
# Monkey patch ElementTree 
import xml.etree.ElementTree as ET 

def _serialize_xml(write, elem, encoding, qnames, namespaces): 
    tag = elem.tag 
    text = elem.text 
    if tag is ET.Comment: 
     write("<!--%s-->" % ET._encode(text, encoding)) 
    elif tag is ET.ProcessingInstruction: 
     write("<?%s?>" % ET._encode(text, encoding)) 
    else: 
     tag = qnames[tag] 
     if tag is None: 
      if text: 
       write(ET._escape_cdata(text, encoding)) 
      for e in elem: 
       _serialize_xml(write, e, encoding, qnames, None) 
     else: 
      write("<" + tag) 
      items = elem.items() 
      if items or namespaces: 
       if namespaces: 
        for v, k in sorted(namespaces.items(), 
             key=lambda x: x[1]): # sort on prefix 
         if k: 
          k = ":" + k 
         write(" xmlns%s=\"%s\"" % (
          k.encode(encoding), 
          ET._escape_attrib(v, encoding) 
          )) 
       #for k, v in sorted(items): # lexical order 
       for k, v in items: # Monkey patch 
        if isinstance(k, ET.QName): 
         k = k.text 
        if isinstance(v, ET.QName): 
         v = qnames[v.text] 
        else: 
         v = ET._escape_attrib(v, encoding) 
        write(" %s=\"%s\"" % (qnames[k], v)) 
      if text or len(elem): 
       write(">") 
       if text: 
        write(ET._escape_cdata(text, encoding)) 
       for e in elem: 
        _serialize_xml(write, e, encoding, qnames, None) 
       write("</" + tag + ">") 
      else: 
       write(" />") 
    if elem.tail: 
     write(ET._escape_cdata(elem.tail, encoding)) 

ET._serialize_xml = _serialize_xml 

from collections import OrderedDict 

class OrderedXMLTreeBuilder(ET.XMLTreeBuilder): 
    def _start_list(self, tag, attrib_in): 
     fixname = self._fixname 
     tag = fixname(tag) 
     attrib = OrderedDict() 
     if attrib_in: 
      for i in range(0, len(attrib_in), 2): 
       attrib[fixname(attrib_in[i])] = self._fixtext(attrib_in[i+1]) 
     return self._target.start(tag, attrib) 


tree = ET.parse("example1.xml", OrderedXMLTreeBuilder()) 
tree.write("new-data.xml") 

xmlはどのようにソートされますか?

答えて

1

は、ソートのキーとして各<image>タグのfile属性を使用するlist.sortためkey名前付き引数を使用します。

キーは、各リストから比較キーを抽出するために使用される1つの引数の関数を指定します要素(たとえば、key = str.lower)。リスト内の各項目に対応するキーが1回計算された後、ソート処理全体に使用されます。デフォルト値Noneは、リスト項目が別のキー値を計算せずに直接ソートされることを意味します。 lxmlは、それらが(xmlとは違って)に設定されているために、属性をシリアル化していることを指摘this answerのオフに基づいてlxmlを使用して

import xml.etree.ElementTree 

xml_string = r'''<?xml-stylesheet type='text/xsl' href='image_metadata_stylesheet.xsl'?> 
<dataset> 
    <name>imglab dataset</name> 
    <comment>Created by imglab tool.</comment> 
    <images> 
    <image file="/home/iris/Documents/SONY_MAX-20150408-200026-210358-00003.jpg"> 
     <box top="175" left="59" width="73" height="29"> 
     <label>groundpainting_hotstar</label> 
     </box> 
     <box top="174" left="205" width="56" height="24"> 
     <label>groundpainting_yesbank</label> 
     </box> 
     <box top="170" left="141" width="44" height="32"> 
     <label>groundpainting_vodafone</label> 
     </box> 
    </image> 
    <image file="/home/iris/Documents/SONY_MAX-20150408-200026-210358-00001.jpg"/> 
    <image file="/home/iris/Documents/SONY_MAX-20150408-200026-210358-00002.jpg"> 
     <box top="198" left="17" width="32" height="10"> 
     <label>sightscreen_pepsi</label> 
     </box> 
    </image> 
</images> 
</dataset>''' 

root = xml.etree.ElementTree.fromstring(xml_string) 
images_root = root.find('images') 
images = images_root.findall('image') 
images.sort(key = lambda x: x.attrib['file']) 
images_root[:] = images 

print(xml.etree.ElementTree.tostring(root)) 

代替ソリューション:

import lxml.etree 

xml_string = r'''<?xml-stylesheet type='text/xsl' href='image_metadata_stylesheet.xsl'?> 
<dataset> 
    <name>imglab dataset</name> 
    <comment>Created by imglab tool.</comment> 
    <images> 
    <text>lol</text> 
    <image file="/home/iris/Documents/SONY_MAX-20150408-200026-210358-00003.jpg"> 
     <box top="175" left="59" width="73" height="29"> 
     <label>groundpainting_hotstar</label> 
     </box> 
     <box top="174" left="205" width="56" height="24"> 
     <label>groundpainting_yesbank</label> 
     </box> 
     <box top="170" left="141" width="44" height="32"> 
     <label>groundpainting_vodafone</label> 
     </box> 
    </image> 
    <image file="/home/iris/Documents/SONY_MAX-20150408-200026-210358-00001.jpg"/> 
    <image file="/home/iris/Documents/SONY_MAX-20150408-200026-210358-00002.jpg"> 
     <box top="198" left="17" width="32" height="10"> 
     <label>sightscreen_pepsi</label> 
     </box> 
    </image> 
</images> 
</dataset>''' 

root = lxml.etree.fromstring(xml_string) 
images_root = root.find('images') 
images = images_root.findall('image') 
images.sort(key = lambda x: x.attrib['file']) 
images_root[:] = images 

print(lxml.etree.tostring(root)) 

注:これは、すべての子を削除します(直後の子孫)は<image>ではない<images>です。

+0

ありがとうございます。それは働いた...しかし何らかの理由で....これはボックスの属性を再調整する...例えば、元のレイアウトは、高さ、左、上と幅に変更された上、左、幅と高さです。 – popeye

+1

@popeye属性の順序を保持する特別な理由はありますか? [属性の順序はXMLでは重要ではありません](https://stackoverflow.com/questions/14917943/python-lxml-enforcing-a-specific-order-for-attributes) [この回答](https://stackoverflow.com/a/17654556/8955448)は、 'lxml'は属性が' xml'と違って、設定された順序で直列化することを指摘しています。私はこの代替案を含めるために私の答えを更新しました。 – Galen

+0

ありがとう...これは本当に役に立ちます。 – popeye

関連する問題