Как изменить текст вложенных элементов в XML-файле с помощью Python? - PullRequest
0 голосов
/ 18 октября 2019

В настоящее время я работаю над корпусом / набором данных. Это в формате XML, как вы можете видеть на рисунке ниже. Я столкнулся с проблемой. Я хочу получить доступ ко всем 'ne' элементам один за другим, как показано на рисунке ниже. Затем я хочу получить доступ к тексту элементов 'W' , которые находятся внутри элементов 'ne'. Затем я хочу объединить твои символы 'SDi' и 'EDi' с текстом этих элементов 'W'. 'i' может принимать любое положительное целое число, начиная с 1. В случае 'SDi' мне нужен только текст первого элемента 'W' , который находится внутри элемента 'ne'. В случае 'EDi' мне нужен только текст последнего элемента 'W' , который находится внутри элемента 'ne'. В настоящее время я не получаю ничего в качестве вывода после запуска кода. Я думаю, что это из-за того, что к элементу «W» никогда не обращаются. Более того, я думаю, что к элементу 'W' нет доступа, потому что он является внуком элемента 'ne', поэтому к нему нельзя получить прямой доступ, скорее это возможно с помощью его родительского узла.

Примечание1:Количество и имена вложенных элементов внутри элементов 'ne' не совпадают.

Примечание 2: Здесь объясняются только те вещи, которые необходимы. Вы можете найти некоторые другие детали в кодировке / картинке, но игнорируйте их.

Я использую Spyder (python 3.6). Любая помощь будет принята с благодарностью.

Ниже приведено изображение из файла XML, над которым я работаю: enter image description here

Текстовая версия файла XML: Нажмите здесь

Пример / ожидаемое выходное изображение (ниже): enter image description here

Кодирование, которое я сделал до сих пор:

for i in range(len(List_of_root_nodes)):
true_false = True
current = List_of_root_nodes[i]
start_ID = current.PDante_ID
#print('start:', start_ID)  # For Testing
end_ID = None
number = str(i+1)  # This number will serve as i used with SD and ED that is (SDi and EDi)

discourse_starting_symbol = "SD" + number
discourse_ending_symbol = "ED" + number

while true_false:    
    if current.right_child is None:        
        end_ID = current.PDante_ID
        #print('end:', end_ID)  # For Testing
        true_false = False        
    else:        
        current = current.right_child

# Finding 'ne' element with id='start_ID'
ne_text = None
ne_id = None

for ne in myroot.iter('ne'):    
    ne_id = ne.get('id')

    # If ne_id matches with start_ID means the place where SDi is to be placed is found    
    if ne_id == start_ID:        
        for w in ne.iter('W'):            
            ne_text = str(w.text)            
            boundary_and_text = " " + str(discourse_starting_symbol) + " " + ne_text
            w.text = boundary_and_text
            break

    # If ne_id matches with end_ID means the place where EDi is to be placed is found

    # Some changes Required here: Here the 'EDi' will need to be placed after the last 'W' element.
    # So last 'W' element needs to be accessed
    if ne_id == end_ID:        
        for w in ne.iter('W'):            
            ne_text = str(w.text)            
            boundary_and_text = ne_text + " " + str(discourse_ending_symbol) + " "
            w.text = boundary_and_text
            break

Ответы [ 2 ]

1 голос
/ 19 октября 2019

Примерно так (a.xml - это загруженный вами XML):

Обратите внимание, что код не использует никакую внешнюю библиотеку.

import xml.etree.ElementTree as ET

SD = 'SD'
ED = 'ED'

root = ET.parse('a.xml')

counter = 1

for ne in root.findall('.//ne'):
    w_lst = ne.findall('.//W')
    if w_lst:
        w_lst[0].text = '{}{} {}'.format(SD, counter, w_lst[0].text)
        if len(w_lst) > 1:
            w_lst[-1].text = '{} {}{}'.format(w_lst[-1].text, ED, counter)
        counter += 1
ET.dump(root)
1 голос
/ 19 октября 2019

Всякий раз, когда вам нужно изменить XML с различными нюансами, рассмотрите XSLT , язык специального назначения, предназначенный для преобразования файлов XML. Вы можете запускать сценарии XSLT 1.0 с помощью стороннего модуля Python lxml (не встроенного etree).

В частности, вызовите преобразование идентичности , чтобы скопировать XML как есть, и затем запустите два шаблона, чтобы добавить SDI к тексту самого первого <W> и самого последнего EDI к тексту последнего<W>. Решение будет работать, если есть 10 или 10 000 <W> узлов, глубоко вложенных или нет.

Чтобы продемонстрировать на примере данных лучших пользователей StackOverflow Python и XSLT, см. online demo , где SDI и EDI добавлены к первому и последнему <user> узлу:

XSLT (сохранить как файл .xsl, специальный файл .xml для загрузки в Python)

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

  <!-- IDENTITY TRANSFORM -->    
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <!-- EDIT FIRST W NODE -->    
  <xsl:template match="W[count(preceding::W)=0]">
    <xsl:copy>
      <xsl:copy-of select="@*"/>
      <xsl:value-of select="concat('SDI ', text())"/>
    </xsl:copy>
  </xsl:template>

  <!-- EDIT LAST W NODE -->    
  <xsl:template match="W[count(preceding::W)+1 = count(//W)]">
    <xsl:copy>
      <xsl:copy-of select="@*"/>
      <xsl:value-of select="concat('EDI ', text())"/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

Python (без циклов или логики if / else)

import lxml.etree as et

doc = et.parse('/path/to/Input.xml')
xsl = et.parse('/path/to/Script.xsl')

# CONFIGURE TRANSFORMER
transform = et.XSLT(xsl)    

# TRANSFORM SOURCE DOC
result = transform(doc)

# OUTPUT TO CONSOLE
print(result)

# SAVE TO FILE
with open('Output.xml', 'wb') as f:
    f.write(result)
...