إنتاج بيانات السياق للتكرارات الأولى والأخيرة لكل قيمة لعنصر ما

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

  •  03-07-2019
  •  | 
  •  

سؤال

بالنظر إلى ملف XML التالي:

<container>
    <val>2</val>
    <id>1</id>
</container>
<container>
    <val>2</val>
    <id>2</id>
</container>
<container>
    <val>2</val>
    <id>3</id>
</container>
<container>
    <val>4</val>
    <id>1</id>
</container>
<container>
    <val>4</val>
    <id>2</id>
</container>
<container>
    <val>4</val>
    <id>3</id>
</container>

أود أن أعود بشيء من هذا القبيل

2 - 1
2 - 3
4 - 1
4 - 3

باستخدام مجموعة العقدة تمكنت من الحصول على آخر تكرار عبر:

exsl:node-set($list)/container[not(val = following::val)]

ولكن لا أستطيع معرفة كيفية الحصول على الأول.

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

المحلول

للحصول على التواجد الأول والأخير (ترتيب المستند) في كل "<val>"المجموعة، يمكنك استخدام <xsl:key> مثله:

<xsl:stylesheet 
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
  <xsl:output method="text" />

  <xsl:key name="ContainerGroupByVal" match="container" use="val" />

  <xsl:variable name="ContainerGroupFirstLast" select="//container[
    generate-id() = generate-id(key('ContainerGroupByVal', val)[1])
    or
    generate-id() = generate-id(key('ContainerGroupByVal', val)[last()])
  ]" />

  <xsl:template match="/">
    <xsl:for-each select="$ContainerGroupFirstLast">
      <xsl:value-of select="val" />
      <xsl:text> - </xsl:text>
      <xsl:value-of select="id" />
      <xsl:value-of select="'&#10;'" /><!-- LF -->
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

التعديل رقم 1:بعض الشرح لأن هذا قد لا يكون واضحًا على الفور:

  • ال <xsl:key> يعود كل شيء <container> العقد وجود معين <val>.أنت تستخدم key() وظيفة للاستعلام عنها.
  • ال <xsl:variable> هو المكان الذي يحدث فيه كل شيء.يقرأ على النحو التالي:
    • لكل من <container> العقد في المستند ("//container") يفحص…
    • …إذا كان لديه نفس المعرف الفريد (generate-id()) باعتبارها العقدة الأولى التي تم إرجاعها بواسطة key() أو العقدة الأخيرة التي تم إرجاعها بواسطة key()
    • أين key('ContainerGroupByVal', val) إرجاع مجموعة من <container> العقد مطابقة للتيار <val>
    • إذا كانت المعرفات الفريدة متطابقة، قم بتضمين العقدة في التحديد
  • ال <xsl:for-each> يفعل الإخراج.يمكن أن يكون كذلك <xsl:apply-templates>.

التعديل رقم 2:كما أشار ديميتري نوفاتشيف بحق في التعليقات، يجب أن تكون حذرًا من استخدام "//"اختصار XPath.إذا كان بإمكانك تجنب ذلك، فافعل ذلك بكل الوسائل - ويرجع ذلك جزئيًا إلى أنه من المحتمل أن يحدد العقد التي لا تريدها، ويرجع ذلك أساسًا إلى أنه أبطأ من تعبير XPath الأكثر تحديدًا.على سبيل المثال، إذا كان المستند الخاص بك يبدو كما يلي:

<containers>
  <container><!-- ... --></container>
  <container><!-- ... --></container>
  <container><!-- ... --></container>
</containers>

ثم يجب عليك استخدام "/containers/container" أو "/*/container" بدلاً من "//container".


التعديل رقم 3:بناء الجملة البديل لما سبق سيكون:

<xsl:stylesheet 
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
  <xsl:output method="text" />

  <xsl:key name="ContainerGroupByVal" match="container" use="val" />

  <xsl:variable name="ContainerGroupFirstLast" select="//container[
    count(
        .
      | key('ContainerGroupByVal', val)[1]
      | key('ContainerGroupByVal', val)[last()]
    ) = 2
  ]" />

  <xsl:template match="/">
    <xsl:for-each select="$ContainerGroupFirstLast">
      <xsl:value-of select="val" />
      <xsl:text> - </xsl:text>
      <xsl:value-of select="id" />
      <xsl:value-of select="'&#10;'" /><!-- LF -->
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

توضيح:مشغل الاتحاد XPath "|"يجمع وسيطاته في مجموعة عقدة.بحكم التعريف، لا يمكن لمجموعة العقد أن تحتوي على عقد مكررة - على سبيل المثال:". | . | ." سيقوم بإنشاء مجموعة عقدة تحتوي على عقدة واحدة بالضبط (العقدة الحالية).

وهذا يعني أنه إذا قمنا بإنشاء مجموعة عقدة موحدة من العقدة الحالية (".")، فإن "key(…)[1]"العقدة و"key(…)[last()]"العقدة، سيكون عدد العقد 2 إذا (وفقط إذا) كانت العقدة الحالية تساوي إحدى العقدتين الأخريين، وفي جميع الحالات الأخرى سيكون العدد 3.

نصائح أخرى

كسباث :

//container[position() = 1]  <- this is the first one
//container[position() = last()]  <- this is the last one

وفيما يلي مجموعة من كسباث وظائف في مزيد من التفاصيل.

أنا.XSLT 1.0

في الأساس هو نفس الحل الذي قدمه Tomalak، ولكنه أكثر قابلية للفهم كما أنه مكتمل، لذا ما عليك سوى نسخ ولصق مستند XML والتحويل ثم الضغط على زر "تحويل" في XSLT IDE المفضل لديك:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

    <xsl:key name="kContByVal" match="container"
     use="val"/>

    <xsl:template match="/*">
      <xsl:for-each select=
       "container[generate-id()
                 =
                  generate-id(key('kContByVal',val)[1])
                 ]
       ">

       <xsl:variable name="vthisvalGroup"
        select="key('kContByVal', val)"/>

       <xsl:value-of select=
        "concat($vthisvalGroup[1]/val,
              '-',
              $vthisvalGroup[1]/id,
              '&#xA;'
              )
      "/>
       <xsl:value-of select=
        "concat($vthisvalGroup[last()]/val,
              '-',
              $vthisvalGroup[last()]/id,
              '&#xA;'
              )
        "/>
      </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

عندما يتم تطبيق هذا التحويل على مستند XML المقدم في الأصل (تم تحريره ليكون جيد التنسيق):

<t>
    <container>
        <val>2</val>
        <id>1</id>
    </container>
    <container>
        <val>2</val>
        <id>2</id>
    </container>
    <container>
        <val>2</val>
        <id>3</id>
    </container>
    <container>
        <val>4</val>
        <id>1</id>
    </container>
    <container>
        <val>4</val>
        <id>2</id>
    </container>
    <container>
        <val>4</val>
        <id>3</id>
    </container>
</t>

يتم الحصول على النتيجة المطلوبة:

2-1
2-3
4-1
4-3

القيام مذكرة:

  1. نحن نستخدم طريقة Muenchian للتجميع للعثور على واحد container عنصر لكل مجموعة من هذه العناصر التي لها نفس القيمة val.
  2. من قائمة العقدة بأكملها container العناصر مع نفس val قيمة, ، نقوم بإخراج البيانات المطلوبة للأول container عنصر في المجموعة وللأخير container عنصر في المجموعة.

ثانيا.اكس اس ال تي 2.0

هذا التحول:

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xsl:output method="text"/>

    <xsl:template match="/*">
      <xsl:for-each-group select="container"
           group-by="val">
        <xsl:for-each select="current-group()[1], current-group()[last()]">
          <xsl:value-of select=
           "concat(val, '-', id, '&#xA;')"/>
        </xsl:for-each>
    </xsl:for-each-group>
    </xsl:template>
</xsl:stylesheet>

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

2-1
2-3
4-1
4-3

القيام مذكرة:

  1. استخدام <xsl:for-each-group> تعليمات XSLT.

  2. استخدام current-group() وظيفة.

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