Frage

Ich habe eine XML-Datei in einer flachen Struktur. Wir kontrollieren nicht das Format dieser XML-Datei muss nur damit umgehen. Ich habe die Felder umbenannt, weil sie hoch Domain spezifisch sind und machen Sie nicht wirklich einen Unterschied für das Problem.

<attribute name="Title">Book A</attribute>
<attribute name="Code">1</attribute>
<attribute name="Author">
   <value>James Berry</value>
   <value>John Smith</value>
</attribute>
<attribute name="Title">Book B</attribute>
<attribute name="Code">2</attribute>
<attribute name="Title">Book C</attribute>
<attribute name="Code">3</attribute>
<attribute name="Author">
    <value>James Berry</value>
</attribute>

Key Dinge zu beachten: die Datei nicht besonders hierarchisch ist. Bücher werden von einem Auftreten eines Attributelements begrenzt mit name = ‚Titel‘. Aber der 'Autor' name = Attributknoten ist optional.

Gibt es eine einfache XPath-Anweisung kann ich verwenden die Autoren des Buches ‚n‘ zu finden? Es ist leicht, den Titel des Buches ‚n‘, aber die Autoren Wert ist optional zu identifizieren. Und man kann nicht einfach den folgenden Autor, weil im Fall von Buch 2, das dem Autor für Buch geben würde 3.

Ich habe eine Zustandsmaschine geschrieben dies als eine Reihe von Elementen zu analysieren, aber ich kann es nicht umhin zu denken, ein Weg gewesen wäre, um direkt die Ergebnisse, die ich will.

War es hilfreich?

Lösung

Wir haben das „Attribut“ Element der @name ‚Autor‘ wollen, die ein „Attribut“ Element @name folgt ‚Titel‘ mit einem Wert von ‚Buch n‘, ohne jedes andere „Attribut“ Element von @name ' Titel‘zwischen ihnen (denn wenn es, dann wird der Autor ein anderes Buch verfasst).

Said anders, es bedeutet, dass wir wollen, einen Autor, von denen die ersten vorhergehenden Titeln (die es „gehört“) ist die eine, die wir suchen:

//attribute[@name='Author']
[preceding-sibling::attribute[@name='Title'][1][contains(.,'Book N')]]

N = C => findet <attribute name="Author"><value>James Berry</value></attribute>

N = B => findet nichts

Tasten und / oder Gruppierungsfunktionen zur Verfügung in XSLT 2.0 würde dies leichter machen (und viel schneller, wenn die Datei groß ist).

(SO Code-Parser scheint zu denken, '//' steht für 'Kommentare', aber in XPath ist es nicht !!! Seufz.)

Andere Tipps

Nun, ich habe verwendet elementtree Daten aus dem obigen XML zu extrahieren. Ich habe diese XML-Datei mit dem Namen in foo.xml gespeichert

from xml.etree.ElementTree import fromstring

def extract_data():
    """Returns list of dict of book and
    its authors."""

    f = open('foo.xml', 'r+')
    xml = f.read()
    elem = fromstring(xml)
    attribute_list = elem.findall('attribute')
    dic = {}
    lst = []

    for attribute in attribute_list:
        if attribute.attrib['name'] == 'Title':
            key = attribute.text
        if attribute.attrib['name'] == 'Author':
            for v in attribute.findall('value'):
                lst.append(v.text)
            value = lst
            lst = []
            dic[key] = value
    return dic

Wenn Sie diese Funktion ausführen Sie diese:

{'Book A': ['James Berry', 'John Smith'], 'Book C': ['James Berry']}

Ich hoffe, das ist das, was Sie suchen. Wenn nicht, dann geben Sie einfach ein bisschen mehr. :)

Wie bambax in seiner Antwort erwähnt, eine Lösung XSLT-Taste ist effizienter , insbesondere für große XML-Dokumente:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes"/>
 <!--                                            -->
 <xsl:key name="kAuthByTitle" 
  match="attribute[@name='Author']"
  use="preceding-sibling::attribute[@name='Title'][1]"/>
 <!--                                            -->
    <xsl:template match="/">
      Book C Author:
      <xsl:copy-of select=
         "key('kAuthByTitle', 'Book C')"/>
  <!--                                            -->
         ====================
      Book B Author:
      <xsl:copy-of select=
         "key('kAuthByTitle', 'Book B')"/>
    </xsl:template>
</xsl:stylesheet>

Wenn die obige Transformation auf diesem XML-Dokument angewandt wird:

<t>
    <attribute name="Title">Book A</attribute>
    <attribute name="Code">1</attribute>
    <attribute name="Author">
        <value>James Berry</value>
        <value>John Smith</value>
    </attribute>
    <attribute name="Title">Book B</attribute>
    <attribute name="Code">2</attribute>
    <attribute name="Title">Book C</attribute>
    <attribute name="Code">3</attribute>
    <attribute name="Author">
        <value>James Berry</value>
    </attribute>
</t>

die korrekte Ausgabe erzeugt wird:

  Book C Author:
  <attribute name="Author">
    <value>James Berry</value>
</attribute>

     ====================
  Book B Author:

Sie beachten Sie, dass die "//" XPath Abkürzung verwenden, sollte so viel wie möglich vermieden werden, da es in der Regel bewirkt, dass das gesamte XML-Dokument auf jeder Auswertung des XPath-Ausdrucks gescannt werden.

Alle Titel auswählen und Vorlage anwenden

<xsl:template match="/">
  <xsl:apply-templates select="//attribute[@name='Title']"/>
</xsl:template>

In der die Vorlage Ausgabe Titel, prüfen Sie, ob neben Titel vorhanden sind. Wenn nicht, folgende Ausgabe Autor. Wenn es nicht vorhanden ist, prüfen Sie, ob die folgenden Autoren Knoten Buch von folgenden die gleichen wie autor Knoten des aktuellen Buches. Wenn es ist, bedeutet es, dass die aktuelle Buch keinen Autor hat:

<xsl:template match="*">
   <book>
     <title><xsl:value-of select="."/></title> 
   <author>
   <xsl:if test="not(following::attribute[@name='Title']) or following::attribute[@name='Author'] != following::attribute[@name='Title']/following::attribute[@name='Author']">
   <xsl:value-of select="following::attribute[@name='Author']"/>
   </xsl:if>
   </author>
   </book>
</xsl:template>

Ich bin nicht sicher, ob Sie wirklich dorthin gehen wollen: die einfachste ich fand, war vom Autor zu gehen, den vorherigen Titel erhält, dann überprüfen, dass der erste Autor oder Titel folgenden war in der Tat ein Titel. Hässlich!

/books/attribute[@name="Author"]
  [preceding-sibling::attribute[@name="Title" and string()="Book B"]
                               [following-sibling::attribute[ @name="Author" 
                                                             or @name="Title"
                                                            ]
                                 [1]
                                 [@name="Author"]
                               ]
  ][1]

(I hinzugefügt, um die Bücher -Tag um die Datei zu wickeln).

ich getestet, dass mit libxml2 BTW, mit xml_grep2 , aber nur auf den Probendaten, die Sie geben, so mehr Tests sind willkommen).

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top