Setel ET.SubElement.text ke dict.value jika dict.key sama dengan node XML lain dalam induk yang sama

Jadi, saya membuat subelemen baru dengan ElementTree di mana teks dari node baru harus berupa nilai dict JIKA kunci dict dari nilai yang sesuai sama dengan teks dari node XML lain dalam Node induk yang sama.

Contoh XML:

<ns0:scaleType xmlns:ns0="http://someURL.com/">
  <scales>
    <scale>
        <names>
            <name id="0">abc</name>
            <name id="1" />
        </names>
        <alternativeExportValues>
        </alternativeExportValues>
    </scale>
    <scale>
        <names>
            <name id="0">def</name>
            <name id="1" />
        </names>
        <alternativeExportValues>
        </alternativeExportValues>
    </scale>
 </scales>
</ns0:scaleType>

Contoh CSV:

name;value
abc;10012
def;20025

Kode Python sekarang:

import xml.etree.ElementTree as ET

import csv

csvData = []

with open('myCSV.csv', 'r', encoding="utf8") as f:
    reader = csv.reader(f, delimiter=";")
    for row in reader:
        csvData.append({'name': row[0], 'value': row[1]})

tree = ET.parse('myXml.xml')
root = tree.getroot()

def my_Function():
    for p in csvData:
        for name in root.findall(".//name[@id='0']"):
            text = name.text
            if p['name'] == text:
                value = p['value']
                return value
my_Function()


for elem in root.iter('alternativeExportValues'):
    newNode = ET.SubElement(elem, 'alternativeExportValue')
    newNode.text = 

tree.write("myNewXML.xml", encoding="utf-8")

Hasil yang diharapkan:

<ns0:scaleType xmlns:ns0="http://someURL.com/">
  <scales>
    <scale>
        <names>
            <name id="0">abc</name>
            <name id="1" />
        </names>
        <alternativeExportValues>
           <alternativeExportValue>10012</alternativeExportValue>
        </alternativeExportValues>
    </scale>
    <scale>
        <names>
            <name id="0">def</name>
            <name id="1" />
        </names>
        <alternativeExportValues>
           <alternativeExportValue>20025</alternativeExportValue>
        </alternativeExportValues>
    </scale>
 </scales>
</ns0:scaleType>


Saya mencoba memasang loop for yang membuat node alternativeExportValue di my_Function, tetapi akhirnya mendapatkan nilai yang sama di newNode.text atau terjebak dalam loop tanpa akhir.

Seperti yang Anda lihat pada hasil yang diharapkan, saya ingin dict.value sebagai teks untuk Node yang baru dibuat jika cocok dengan
<name id="0"> innerText dalam induk yang sama <scale>.


person Alecbalec    schedule 11.12.2019    source sumber


Jawaban (1)


Saya tidak begitu yakin apa yang seharusnya dilakukan my_Function, tetapi pertimbangkan logika berikut:

  • Membaca/memproses data CSV. (Anda sudah melakukan ini, namun pertimbangkan DictReader sebagai gantinya . Ini akan memetakan nilai ke dict menggunakan tombol dari baris pertama.)
  • Proses setiap scale elemen.
  • Buat elemen alternativeExportValue baru dengan nilai "nilai".
  • Periksa apakah elemen name dengan nilai atribut id "0" cocok dengan entri "nama" saat ini.
  • Jika ya, tambahkan elemen alternativeExportValue baru.

Contoh...

import xml.etree.ElementTree as ET
import csv

with open('myCSV.csv', 'r', encoding="utf8") as csvfile:
    tree = ET.parse('myXml.xml')

    for row in csv.DictReader(csvfile, delimiter=";"):
        name = row.get("name")
        new_aev_elem = ET.Element("alternativeExportValue")
        new_aev_elem.text = row.get("value")
        for scale in tree.findall(".//scale"):
            name0 = scale.find("names/name[@id='0']")
            if name0.text == name:
                aevs_elem = scale.find("alternativeExportValues")
                aevs_elem.append(new_aev_elem)
                break

    tree.write("myNewXML.xml", encoding="utf-8")

Ini berfungsi tetapi tidak terlalu efisien karena Anda harus memproses setiap elemen scale yang mendahului elemen scale sebenarnya yang ingin Anda modifikasi.

Lebih buruk lagi, jika Anda menghapus break itu akan memproses setiap elemen scale dalam XML (untuk setiap baris CSV!).

Jika Anda dapat beralih ke lxml, Anda dapat menggunakan XPath* yang sedikit lebih kompleks yang hanya akan memproses scale elemen yang perlu diubah...

from lxml import etree
import csv

with open('myCSV.csv', 'r', encoding="utf8") as csvfile:
    tree = etree.parse('myXml.xml')

    uc = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    lc = "abcdefghijklmnopqrstuvwxyz"

    for row in csv.DictReader(csvfile, delimiter=";"):
        name = row.get("name").lower()
        new_aev_elem = etree.Element("alternativeExportValue")
        new_aev_elem.text = row.get("value")
        aevs_elem = tree.xpath(f".//scale[translate(names/name[@id='0'],'{uc}','{lc}')='{name}']/alternativeExportValues")[0]
        aevs_elem.append(new_aev_elem)

    tree.write("myNewXML.xml", encoding="utf-8")

*Dukungan XPath di ElementTree terbatas .

person Daniel Haley    schedule 12.12.2019
comment
Terima kasih @Daniel Haley. Ide saya dengan my_Function adalah menemukan nilai kunci yang cocok di .//name[@id="0"]. Saya mencoba beralih ke lxml tetapi aevs_elem = tree.xpath(f".//scale[names/name[@id='0']='{name}']/alternativeExportValues")[0] mengembalikan IndexError: list index out of range. Jadi ini berarti xPath tidak ada. - person Alecbalec; 12.12.2019
comment
@Alecbalec - Apakah Anda juga beralih ke DictReader? Jika tidak, Anda mungkin memproses baris pertama CSV dan XPath gagal karena name[@id='0']='name'. Jika Anda tidak ingin beralih ke DictReader, Anda dapat melewati baris pertama CSV atau menggunakan coba/kecuali. Jika Anda telah beralih ke DictReader, pasti ada sesuatu yang berbeda di CSV atau XML Anda karena saya menguji dengan apa yang ada di pertanyaan Anda dan tidak mendapatkan kesalahan apa pun. - person Daniel Haley; 12.12.2019
comment
Pertama-tama, terima kasih banyak! Masalah pertama adalah pengkodean saat membaca file csv. Dalam loop for saya menjalankan print(row) untuk melihat output dan mengembalikan '\ufeffname': bukannya name:. Mengubah pengkodean menjadi "utf-8-sig" dan segalanya tampak lebih baik. Saya menghapus komentar pada kode dan menambahkan print(get.('value') untuk melihat di baris mana dalam file .csv kode tersebut benar-benar rusak. Setelah melihat sekilas, terlihat jelas bahwa xPath peka huruf besar-kecil dan itulah mengapa ia mengembalikan IndexError: list index out of range - person Alecbalec; 12.12.2019
comment
@Alecbalec - Ahh ya XPath jelas peka terhadap huruf besar-kecil. Memaksa nilai menjadi huruf besar atau kecil agak menyusahkan di XPath 1.0 (yang didukung lxml), tapi saya akan memperbarui jawaban lxml saya sehingga tidak peka huruf besar-kecil. - person Daniel Haley; 12.12.2019
comment
Ya, untungnya hanya ada beberapa baris di mana satu karakter menggunakan huruf kecil di file XML asli dan lebih tinggi di csv. Sekali lagi terimakasih. - person Alecbalec; 12.12.2019