web-dev-qa-db-fra.com

Python version 2.7: XML ElementTree: Comment parcourir certains éléments d'un élément enfant afin de trouver une correspondance

Je suis un novice en programmation et n'utilise que rarement python, alors s'il vous plaît, supportez-moi pendant que j'essaie d'expliquer ce que j'essaie de faire :)

J'ai le XML suivant:

<?xml version = "1.0" encoding = "utf-8"?>
<Patients>
    <Patient>
               <PatientCharacteristics>
                   <patientCode>3</patientCode>
               </PatientCharacteristics>
               <Visits>
                   <Visit>
                          <DAS>
                               <CRP>14</CRP>
                               <ESR/>
                               <Joints>
                                       <DAS_PROFILE>28/28</DAS_PROFILE>
                                       <SWOL28>20</SWOL28>
                                       <TEN28>20</TEN28>
                               </Joints>
                          </DAS>
                          <VisitDate>2010-02-17</VisitDate>
                   </Visit>
                   <Visit>
                          <DAS>
                               <CRP>10</CRP>
                               <ESR/>
                               <Joints>
                                       <DAS_PROFILE>28/28</DAS_PROFILE>
                                       <SWOL28>15</SWOL28>
                                       <TEN28>20</TEN28>
                               </Joints>
                          </DAS>
                          <VisitDate>2010-02-10</VisitDate>
                   </Visit>
               </Visits>
    </Patient>
    <Patient>
        <PatientCharacteristics>
                   <patientCode>3</patientCode>
        </PatientCharacteristics>
               <Visits>
                   <Visit>
                          <DAS>
                               <CRP>14</CRP>
                               <ESR/>
                               <Joints>
                                       <DAS_PROFILE>28/28</DAS_PROFILE>
                                       <SWOL28>34</SWOL28>
                                       <TEN28>0</TEN28>
                               </Joints>
                          </DAS>
                          <VisitDate>2010-08-17</VisitDate>
                   </Visit>
                   <Visit>
                          <DAS>
                               <CRP>10</CRP>
                               <ESR/>
                               <Joints>
                                       <DAS_PROFILE>28/28</DAS_PROFILE>
                                       <SWOL28></SWOL28>
                                       <TEN28>2</TEN28>
                               </Joints>
                          </DAS>
                          <VisitDate>2010-07-10</VisitDate>
                   </Visit>
                   <Visit>
                          <DAS>
                               <CRP>9</CRP>
                               <ESR/>
                               <Joints>
                                       <DAS_PROFILE>28/28</DAS_PROFILE>
                                       <SWOL28>56</SWOL28>
                                       <TEN28>6</TEN28>
                               </Joints>
                          </DAS>
                          <VisitDate>2009-07-10</VisitDate>
                   </Visit>
               </Visits>

    </Patient>
</Patients>

Tout ce que je veux faire ici, c'est mettre à jour certaines valeurs 'SWOL28' si elles correspondent aux codes patientCode et VisitDate que j'ai enregistrés dans un fichier texte. Si j'ai bien compris, elementtree n'inclut pas de référence parent, comme si c'était le cas, je pourrais simplement utiliser findall () à partir de la racine et travailler à l'envers à partir de là. En l'état, voici mon psuedocode:

  1. Pour chaque ligne du fichier texte: 
  2. Insérez Visit_Date Patient_Code New_SWOL28 dans des variables
  3. Pour chaque élément patient:
  4. Si patientCode = Patient_Code
  5. Pour chaque élément de visite:
  6. Si VisitDate = Visit_Date
  7. Si l'élément SWOL28 existe pour cette visite
  8. Mettre à jour SWOL28 à New_SWOL28

Mais je suis bloqué à l'étape 5. Comment puis-je obtenir une liste des visites à parcourir? Toutes mes excuses si c'est une question très stupide, mais j'ai cherché haut et bas pour une réponse, je vous assure! J'ai réduit mon code à l'exemple de la partie à corriger ci-dessous:

import xml.etree.ElementTree as ET
tree = ET.parse('DB3.xml')
root = tree.getroot()
for child in root: # THIS GETS ME ALL THE PATIENT ATTRIBUTES
    print child.tag 
    for x in child/Visit: # THIS IS WHAT I CANNOT FIND THE CORRECT SYNTAX FOR
        # I WOULD THEN PERFORM STEPS 6, 7 AND 8 HERE

Je serais profondément reconnaissant de toute idée que chacun de vous pourrait avoir à ce sujet. Je ne suis pas une programmation naturelle, c'est sûr!

Merci d'avance, Sarah

Modifier 1:

Sur les conseils de SVK ci-dessous, j'ai essayé les solutions suivantes:

import xml.etree.ElementTree as ET
tree = ET.parse('Untitled.xml')
root = tree.getroot()
for child in root:
    print child.tag 
    child.find( "visits" )
    for x in child.iter("visit"):
        print x.tag, x.text

Mais le seul résultat que je reçois est: Patient Patient Et aucune des balises inférieures. Des idées?

9
Sarah-Ann

Cela n'a pas été testé, cela devrait être assez proche de ce que vous voulez.

for patient in root:
    patient_code =  patient.find('PatientCharacteristics').find('patientCode')
    if patient_code.text == code:
            for visit in patient.find('Visits'):
                    visit_date = visit.find('VisitDate')
                    if visit_date.text == date:
                        swol28 = visit.find('DAS').find('Joints').find('SWOL28')
                        if swol28.text:
                            visit.find('DAS').find('Joints').set('SWOL28', new_swol28)
4
Peter Enns

Vous pouvez parcourir toutes les balises "visit" directement sous un élément "element" comme ceci:

for x in element.iter("visit"):

Vous pouvez trouver le premier enfant direct de l'élément correspondant à une balise donnée avec:

element.find( "visits" )

Il semble que vous deviez d'abord localiser l'élément "visites", qui est le parent de "visite", puis parcourir les enfants "visiter". En les réunissant, vous obtiendrez quelque chose comme ceci:

for patient_element in root:
    print patient_element.tag 
    visits_element = patient_element.find( "visits" )
    for visit_element in visits_element.iter("visit"):
        print visit_element.tag, visit_element.text
        # ... further processing of each visit element here

De manière générale, consultez la section "Recherche d'éléments intéressants" dans la documentation de xml.etree.ElementTree: http://docs.python.org/2/library/xml.etree.elementtree.html#finding-interesting-elements

5
svk

Vous pouvez utiliser un CssSelector pour obtenir les nœuds souhaités à partir de l'élément Patient:

from lxml.cssselect import CSSSelector
visitSelector = CSSSelector('Visit')
visits =  visitSelector(child)

vous pouvez faire de même pour obtenir les balises patientCode et SWOL28 . Vous pouvez ensuite accéder au texte des éléments et le modifier à l'aide de element.text.

0
niroyb

Si vous utilisez lxml.etree, vous pouvez utiliser xpath pour rechercher les éléments à mettre à jour.

Par exemple.

doc.xpath('Patient[PatientCharacteristics/patientCode=$patient]/Visits/Visit[VisitDate=$visit]',patient="3",visit="2009-07-10")

Alors 

from lxml import etree

doc = etree.parse("DB3.xml")

changes = [
  dict(patient='3',visit='2010-08-17',swol28="99"),
]

def update_doc(x,d):
  for row in d:
    for visit in x.xpath('Patient[PatientCharacteristics/patientCode=$patient]/Visits/Visit[VisitDate=$visit]',**row):
      for swol28 in visit.xpath('DAS/Joints/SWOL28'):
        swol28.text = row['swol28']

update_doc(doc,changes)

print etree.tostring(doc)

Devrait vous donner quelque chose qui contient:

<Patient>
  <PatientCharacteristics>
    <patientCode>3</patientCode>
  </PatientCharacteristics>
  <Visits>
    <Visit>
      <DAS>
      <CRP>14</CRP>
      <ESR/>
      <Joints>
        <DAS_PROFILE>28/28</DAS_PROFILE>
        <SWOL28>99</SWOL28>
        <TEN28>0</TEN28>
      </Joints>
    </DAS>
    <VisitDate>2010-08-17</VisitDate>
    </Visit>
  </Visits>
</Patient>
0
MattH