الانتقال إلى العقد باستخدام xpath في الهيكل المسطح

StackOverflow https://stackoverflow.com/questions/614370

  •  03-07-2019
  •  | 
  •  

سؤال

لدي ملف xml في بنية مسطحة.نحن لا نتحكم في شكل ملف xml هذا فقط لديك للتعامل معها.لقد سميت المجالات لأنها شديدة المجال محددة و لا حقا جعل أي اختلاف إلى مشكلة.

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

مفتاح الأشياء ملاحظة:الملف لا سيما الهرمية.الكتب هي محددة بواسطة التكرار سمة العنصر name='Title'.ولكن name='Author' السمة عقدة هو اختياري.

هل هناك بسيطة xpath بيان يمكنني العثور على الكتاب من كتاب 'n'?فمن السهل لتحديد عنوان الكتاب 'n', ولكن الكتاب قيمة اختيارية.و لا يمكنك أن تأخذ التالية المؤلف لأنه في حالة الكتاب 2 ، وهذا من شأنه أن يعطي المؤلف للكتاب 3.

كنت قد كتبت آلة الدولة إلى تحليل هذه سلسلة من العناصر, ولكن أنا لا يمكن أن تساعد في التفكير كان هناك طريقة مباشرة تحصل على النتائج التي كنت تريد.

هل كانت مفيدة؟

المحلول

نريد "سمة" عنصر @اسم "الكاتب" التي بعد "سمة" عنصر @اسم 'Title' مع قيمة كتاب n' دون أي "سمة" عنصر @اسم 'Title' بينهما (لأنه إذا كان هناك إذن المؤلف في تأليف بعض الكتب).

قال بشكل مختلف ، فهذا يعني أننا نريد المؤلف الذي أولا السابقة عنوان (واحد أنه "ينتمي إلى") هو واحد نحن نبحث عن:

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

N=C => يجد <attribute name="Author"><value>James Berry</value></attribute>

ن=ب => لا يجد شيء

استخدام مفاتيح و/أو تجميع الوظائف المتاحة في XSLT 2.0 من شأنه أن يجعل هذا أسهل (و أسرع بكثير إذا كان الملف كبير).

(حتى رمز محلل تعتقد '//' لتقف على 'تعليقات' ولكن في XPath انها ليست!!!تنفس الصعداء.)

نصائح أخرى

حسنا، لقد استخدمت Elementtree لاستخراج البيانات من XML أعلاه. لقد حفظ هذا XML في ملف اسمه foo.xml

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

عند تشغيل هذه الوظيفة ستحصل على هذا:

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

وآمل أن يكون هذا هو ما كنت أبحث عنه. ثم إن لم يكن مجرد تحديد أكثر قليلا. :)

على كما <م> bambax لوحظ في إجابته، والحل باستخدام مفاتيح XSLT أكثر كفاءة ، أو خاصة للمستندات XML كبيرة:

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

عند تطبيق التحول أعلاه على هذه الوثيقة XML:

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

ويتم إنتاج الإخراج الصحيح:

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

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

على هل لاحظ أن استخدام "//" كسباث اختصار يجب تجنبها قدر الإمكان ، أو لأنه عادة ما يتسبب في وثيقة XML كاملة ليتم فحصها على كل تقييم تعبير XPath.

وحدد كافة عناوين وتطبيق قالب

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

في عنوان إخراج القالب، تحقق مما إذا كان عنوان المقبل موجودة. إن لم يكن، خرج يتابع المؤلف. إذا كان لا وجود لها، معرفة ما اذا كان عقدة المؤلف التالية التالية الكتاب هو نفسه على النحو التالي عقدة مؤلف الكتاب الحالي. إذا كان كذلك، فهذا يعني أن الكتاب الحالي لا يوجد لديه المؤلف:

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

وأنا لست متأكد من أنك تريد حقا أن يذهب هناك: أبسط وجدته هو أن يذهب من المؤلف، الحصول على اللقب السابق، ثم تأكد من أن المؤلف الأول أو العنوان التالي كان في الواقع عنوانا. قبيحة!

/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]

و(أنا واضاف <م> كتب العلامة بالالتفاف حول الملف).

واختبرت أنه مع libxml2 راجع للشغل، وذلك باستخدام xml_grep2 ، ولكن فقط على بيانات عينة ما قدمتموه، لذلك لمزيد من الفحوص هي موضع ترحيب).

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top