J'utilise Python (minidom) pour analyser un fichier XML qui imprime une structure hiérarchique qui ressemble à ceci (l'indentation est utilisée ici pour montrer la relation hiérarchique significative):
My Document
Overview
Basic Features
About This Software
Platforms Supported
Au lieu de cela, le programme itère plusieurs fois sur les nœuds et produit les éléments suivants, en imprimant les nœuds en double. (En regardant la liste des nœuds à chaque itération, il est évident pourquoi il fait cela, mais je n'arrive pas à trouver un moyen d'obtenir la liste des nœuds que je recherche.)
My Document
Overview
Basic Features
About This Software
Platforms Supported
Basic Features
About This Software
Platforms Supported
Platforms Supported
Voici le fichier source XML:
<?xml version="1.0" encoding="UTF-8"?>
<DOCMAP>
<Topic Target="ALL">
<Title>My Document</Title>
</Topic>
<Topic Target="ALL">
<Title>Overview</Title>
<Topic Target="ALL">
<Title>Basic Features</Title>
</Topic>
<Topic Target="ALL">
<Title>About This Software</Title>
<Topic Target="ALL">
<Title>Platforms Supported</Title>
</Topic>
</Topic>
</Topic>
</DOCMAP>
Voici le programme Python:
import xml.dom.minidom
from xml.dom.minidom import Node
dom = xml.dom.minidom.parse("test.xml")
Topic=dom.getElementsByTagName('Topic')
i = 0
for node in Topic:
alist=node.getElementsByTagName('Title')
for a in alist:
Title= a.firstChild.data
print Title
Je pourrais résoudre le problème en n'imbriquant pas les éléments 'Topic', en changeant les noms de sujet de niveau inférieur en quelque chose comme 'SubTopic1' et 'SubTopic2'. Mais, je veux profiter de la structuration hiérarchique XML intégrée sans avoir besoin de noms d'éléments différents; il semble que je devrais être en mesure d'imbriquer des éléments "Topic" et qu'il devrait y avoir un moyen de savoir à quel niveau "Topic" je regarde actuellement.
J'ai essayé un certain nombre de fonctions XPath différentes sans grand succès.
getElementsByTagName est récursif, vous obtiendrez tous descendants avec un tagName correspondant. Parce que vos sujets contiennent d'autres sujets qui ont également des titres, l'appel obtiendra plusieurs fois les titres inférieurs.
Si vous souhaitez demander uniquement tous les enfants directs correspondants et que vous n'avez pas XPath disponible, vous pouvez écrire un filtre simple, par exemple:
def getChildrenByTagName(node, tagName):
for child in node.childNodes:
if child.nodeType==child.ELEMENT_NODE and (tagName=='*' or child.tagName==tagName):
yield child
for topic in document.getElementsByTagName('Topic'):
title= list(getChildrenByTagName('Title'))[0] # or just get(...).next()
print title.firstChild.data
Permettez-moi de mettre ce commentaire ici ...
Merci pour la tentative. Ça n'a pas marché mais ça m'a donné quelques idées. Les travaux suivants (la même idée générale; FWIW, le nodeType est ELEMENT_NODE):
import xml.dom.minidom
from xml.dom.minidom import Node
dom = xml.dom.minidom.parse("docmap.xml")
def getChildrenByTitle(node):
for child in node.childNodes:
if child.localName=='Title':
yield child
Topic=dom.getElementsByTagName('Topic')
for node in Topic:
alist=getChildrenByTitle(node)
for a in alist:
# Title= a.firstChild.data
Title= a.childNodes[0].nodeValue
print Title
Je pense que cela peut aider
import os
import sys
import subprocess
import base64,xml.dom.minidom
from xml.dom.minidom import Node
f = open("file.xml",'r')
data = f.read()
i = 0
doc = xml.dom.minidom.parseString(data)
for topic in doc.getElementsByTagName('Topic'):
title= doc.getElementsByTagName('Title')[i].firstChild.nodeValue
print title
i +=1
Sortie:
My Document
Overview
Basic Features
About This Software
Platforms Supported
Vous pouvez utiliser le générateur suivant pour parcourir la liste et obtenir des titres avec des niveaux d'indentation:
def f(elem, level=-1):
if elem.nodeName == "Title":
yield elem.childNodes[0].nodeValue, level
Elif elem.nodeType == elem.ELEMENT_NODE:
for child in elem.childNodes:
for e, l in f(child, level + 1):
yield e, l
Si vous le testez avec votre fichier:
import xml.dom.minidom as minidom
doc = minidom.parse("test.xml")
list(f(doc))
vous obtiendrez une liste avec les tuples suivants:
(u'My Document', 1),
(u'Overview', 1),
(u'Basic Features', 2),
(u'About This Software', 2),
(u'Platforms Supported', 3)
Ce n'est qu'une idée de base à affiner bien sûr. Si vous voulez juste des espaces au début, vous pouvez coder cela directement dans le générateur, mais avec le niveau, vous avez plus de flexibilité. Vous pouvez également détecter automatiquement le premier niveau (ici, c'est juste un mauvais travail d'initialiser le niveau à -1 ...).
Fonction récursive:
import xml.dom.minidom
def traverseTree(document, depth=0):
tag = document.tagName
for child in document.childNodes:
if child.nodeType == child.TEXT_NODE:
if document.tagName == 'Title':
print depth*' ', child.data
if child.nodeType == xml.dom.Node.ELEMENT_NODE:
traverseTree(child, depth+1)
filename = 'sample.xml'
dom = xml.dom.minidom.parse(filename)
traverseTree(dom.documentElement)
Votre xml:
<?xml version="1.0" encoding="UTF-8"?>
<DOCMAP>
<Topic Target="ALL">
<Title>My Document</Title>
</Topic>
<Topic Target="ALL">
<Title>Overview</Title>
<Topic Target="ALL">
<Title>Basic Features</Title>
</Topic>
<Topic Target="ALL">
<Title>About This Software</Title>
<Topic Target="ALL">
<Title>Platforms Supported</Title>
</Topic>
</Topic>
</Topic>
</DOCMAP>
Votre sortie souhaitée:
$ python parse_sample.py
My Document
Overview
Basic Features
About This Software
Platforms Supported