2009-10-20 2 views
14

Я использую Python (minidom) для синтаксического анализа файла XML, который печатает иерархическую структуру, которая выглядит примерно так (отступы используются здесь, чтобы показать существенные иерархические отношения):XML Синтаксического с Python и minidom

My Document 
Overview 
    Basic Features 
    About This Software 
     Platforms Supported 

Вместо этого программа повторяется несколько раз над узлами и производит следующее, дублируя узлы. (Глядя на список узлов на каждой итерации, то понятно, почему он делает это, но я не могу найти способ, чтобы получить список узлов Я ищу.)

My Document 
Overview 
Basic Features 
About This Software 
Platforms Supported 
Basic Features 
About This Software 
Platforms Supported 
Platforms Supported 

Вот исходный 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> 

Вот программа 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 

я мог бы решить эту проблему, не вложенности элементов «темы», меняя имена ниже уровня тему на что-то вроде «SubTopic1» и ' SubTopic2. Но я хочу использовать встроенное XML-иерархическое структурирование без необходимости использования разных имен элементов; кажется, что я должен иметь возможность встраивать элементы «Тема» и что должен быть какой-то способ узнать, на каком уровне «Тема» я сейчас смотрю.

Я пробовал несколько различных функций XPath без особого успеха.

+0

Если вы хотите выход первых вы можете просто напечатать текст из каждого элемента - Я не ясно, как structuting влияет на разыскиваемый OUTPUT – Mark

ответ

8

getElementsByTagName рекурсивный, вы получите всех потомков с соответствующим tagName. Поскольку ваши Темы содержат другие Темы, которые также имеют Заголовки, вызов будет получать заголовки с понижающим титром много раз.

Если вы хотите задать для всех соответствующих прямых только детей, и у вас нет XPath доступны, вы можете написать простой фильтр, например .:

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 
+0

Спасибо за попытку. Это не сработало, но это дало мне несколько идей. Следующие работы (та же общая идея; FWIW, то NODETYPE является ELEMENT_NODE): импорта xml.dom.minidom из xml.dom.minidom импорт узла йота = xml.dom.minidom.parse ("docmap.xml «) защиту getChildrenByTitle (узел): для ребенка в node.childNodes: если child.localName == 'Название': ребенок выход тема = dom.getElementsByTagName ('тема') для узла в теме: alist = getChildrenByTitle (node) для a in alist: # Заголовок = a.firstChild.data Заголовок = a.childNodes [0] .nodeValue печать Название – hWorks

+0

Ой, да, я имел в виду ELEMENT не ТЕКСТ, конечно! doh, fixed – bobince

7

Позвольте мне поставить этот комментарий здесь .. .

Благодарим за попытку. Это не сработало, но это дало мне несколько идей. Следующие работы (та же общая идея; FWIW, то NODETYPE является 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 
+0

Я бы назвал функцию getTitle (или 'get_title') и не возвратил бы все непосредственные дочерние элементы Title Title, а только первый (так как в любом случае должен быть только один заголовок на ребенка). –

+0

Возможно, это то, чего я не получаю. Мне нужны титулы всех ближайших детей. Возможно, лучшим именем будет getTitlesOfChildren. – hWorks

3

Вы можете использовать следующий генератор для запуска списка и получить заголовки с уровней отступа:

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 

Если вы проверить его с вашим файлом:

import xml.dom.minidom as minidom 
doc = minidom.parse("test.xml") 
list(f(doc)) 

вы получите список со следующими кортежей:

(u'My Document', 1), 
(u'Overview', 1), 
(u'Basic Features', 2), 
(u'About This Software', 2), 
(u'Platforms Supported', 3) 

Это, конечно, только базовая идея. Если вы просто хотите использовать пробелы в начале, вы можете запрограммировать это непосредственно в генераторе, хотя с уровнем, которым вы обладаете большей гибкостью. Вы также можете автоматически обнаружить первый уровень (здесь это всего лишь плохая работа по инициализации уровня до -1 ...).

+0

Точно то, что я пытался сделать весь день, прежде чем приходить на генераторы. Большое спасибо. – hWorks

1

возвратная степенная функция:

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) 

Вашего 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> 

Ваш желаемый результат:

$ python parse_sample.py 
     My Document 
     Overview 
      Basic Features 
      About This Software 
       Platforms Supported 
2

Я думаю, что может помочь

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 

Выход:

My Document 
Overview 
Basic Features 
About This Software 
Platforms Supported