سؤال

لدي سؤال للأشخاص الأذكياء في مجتمع SO.

يوجد أدناه مقتطف من XML تم إنشاؤه بواسطة سيمفوني سي إم إس.

   <news>
        <entry>
            <title>Lorem Ipsum</title>
            <body>
                <p><strong>Lorem Ipsum</strong></p>
                <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed malesuada auctor magna. Vivamus urna justo, pulvinar nec, sagittis malesuada, accumsan in, massa. Quisque mi purus, gravida eget, ultricies a, porta in, sem. Maecenas justo elit, elementum vel, feugiat vulputate, pulvinar nec, velit. Fusce vel ante et diam bibendum euismod. Nunc vel nulla non lorem dignissim placerat. Nulla magna massa, auctor et, tempor nec, auctor sit amet, turpis. Quisque odio lacus, auctor at, posuere id, suscipit eget, dui. Phasellus aliquam. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Proin varius. Phasellus cursus. Cras mattis adipiscing turpis. Sed.</p>
                <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed malesuada auctor magna.</p>
            </body>
        </entry>
    </news>

ما يجب علي فعله هو أخذ جزء من <body> عنصر، بناءً على طول محدد، للعرض في نمط المدونة الخاص بـ:

lorem ipsum dolor sit amet ، incectetur adipiscing leit.سيد malesuada auctor magna.Vivamus Urna Justo ، Pulvinar NEC ، Sagittis Malesuada ، Accumsan In ، Massa.Quisque Mi Purus ، Gravida Eget ، Ultristies A ، Porta In ، Sem ... أكثر

...أين أكثر هو رابط للخبر الكامل.أعلم أنه يمكنني تحديد فقرات معينة وأعلم أيضًا أنه يمكنني استخدام وظيفة السلسلة الفرعية لإحضار عدد محدد من الأحرف.ومع ذلك، أحتاج إلى الحفاظ على تنسيق النص، أي.علامات HTML داخل <body> عنصر.

أدرك أن هذا يثير مشكلات تتعلق بإغلاق العلامة ولكن يجب بالتأكيد أن تكون هناك طريقة.نأمل أن يتمكن شخص أكثر خبرة في استخدام XSLT من إلقاء بعض الضوء على هذه المشكلة.

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

المحلول

ها هي نسختي.لقد قمت باختباره على نموذج XML الخاص بك وهو يعمل.

لاستدعاء ذلك، استخدم <xsl:apply-templates select="path/to/body/*" mode="truncate"/>.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:strip-space elements="*"/>

<!-- limit: the truncation limit -->
<xsl:variable name="limit" select="250"/>

<!-- t: Total number of characters in the set -->
<xsl:variable name="t" select="string-length(normalize-space(//body))"/>

<xsl:template match="*" mode="truncate">
    <xsl:variable name="preceding-strings">
        <xsl:copy-of select="preceding::text()[ancestor::body]"/>
    </xsl:variable>

    <!-- p: number of characters up to the current node -->
    <xsl:variable name="p" select="string-length(normalize-space($preceding-strings))"/>

    <xsl:if test="$p &lt; $limit">
        <xsl:element name="{name()}">
            <xsl:apply-templates select="@*" mode="truncate"/>
            <xsl:apply-templates mode="truncate"/>
        </xsl:element>
    </xsl:if>
</xsl:template>

<xsl:template match="text()" mode="truncate">
    <xsl:variable name="preceding-strings">
        <xsl:copy-of select="preceding::text()[ancestor::body]"/>
    </xsl:variable>

    <!-- p: number of characters up to the current node -->
    <xsl:variable name="p" select="string-length(normalize-space($preceding-strings))"/>

    <!-- c: number of characters including current node -->
    <xsl:variable name="c" select="$p + string-length(.)"/>

    <xsl:choose>
        <xsl:when test="$limit &lt;= $c">
            <xsl:value-of select="substring(., 1, ($limit - $p))"/>
            <xsl:text>&#8230;</xsl:text>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="."/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

<xsl:template match="@*" mode="truncate">
    <xsl:attribute name="{name(.)}"><xsl:value-of select="."/></xsl:attribute>
</xsl:template>

</xsl:stylesheet>

نصائح أخرى

فيما يلي تحويل XSLT 1.0 كامل يحل المشكلة تمامًا.

تحويل XSLT هذا:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ext="http://exslt.org/common"
 xmlns:f="http://fxsl.sf.net/"
 xmlns:myAdd="f:myAdd"
 xmlns:myParam="f:myParam"
 exclude-result-prefixes="ext f myAdd myParam"
>
 <xsl:import href="scanl.xsl"/>
 <!--                                         -->
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>
 <!--                                         -->
 <myAdd:myAdd/>
 <myParam:myParam>0</myParam:myParam>
 <!--                                         -->
 <xsl:param name="pTruncateLength" select="772"/>
 <!--                                         -->
   <xsl:variable name="vFun" select="document('')/*/myAdd:*[1]"/>
   <xsl:variable name="vZero" select="document('')/*/myParam:*[1]"/>
 <!--                                         -->
   <xsl:variable name="vrtfScanResults">
           <xsl:call-template name="scanl">
             <xsl:with-param name="pFun" select="$vFun"/>
             <xsl:with-param name="pQ0" select="$vZero" />
             <xsl:with-param name="pList" select="/*/*/body//text()"/>
           </xsl:call-template>
   </xsl:variable>
 <!--                                         -->
   <xsl:variable name="vScanResults"
        select="ext:node-set($vrtfScanResults)"/>
   <xsl:variable name="vindNode" select=
    "count($vScanResults/*[. > $pTruncateLength][1]
                                   /preceding-sibling::*)"/>
 <!--                                         -->
   <xsl:variable name="vrtfTruncInfo">
       <xsl:for-each select="/*/*/body//text()">
 <!--                                         -->
         <xsl:variable name="vPos" select="position()"/>
         <tNode id="{generate-id()}">
           <xsl:attribute name="preserve">
             <xsl:if test="$vPos &lt; $vindNode">
               <xsl:value-of select="string-length(.)"/>
             </xsl:if>
             <xsl:if test="$vPos > $vindNode">
               <xsl:value-of select="0"/>
             </xsl:if>
             <xsl:if test="$vPos = $vindNode">
               <xsl:value-of select=
               "$vScanResults/*[$vindNode+1]
               -
                $pTruncateLength"/>
             </xsl:if>
           </xsl:attribute>
         </tNode>
       </xsl:for-each>
   </xsl:variable>
 <!--                                         -->
   <xsl:variable name="vTruncInfo" select="ext:node-set($vrtfTruncInfo)"/>
 <!--                                         -->
 <xsl:template match="node()|@*">
   <xsl:copy>
     <xsl:apply-templates select="node()|@*"/>
   </xsl:copy>
 </xsl:template>
 <!--                                         -->
 <xsl:template match="text()[ancestor::body]">
   <xsl:variable name="vAllowedLength"
        select="$vTruncInfo/*[@id = generate-id(current())]/@preserve"
   />
 <!--                                         -->
   <xsl:value-of select="substring(.,1,$vAllowedLength)"/>

   <xsl:if test="string-length(.) > $vAllowedLength
               and
                 $vAllowedLength > 0
                ">
     <strong> ...more</strong>
   </xsl:if>
 </xsl:template>
 <!--                                         -->
 <xsl:template match="myAdd:*" mode="f:FXSL">
   <xsl:param name="pArg1"/>
   <xsl:param name="pArg2"/>
   <xsl:value-of select="$pArg1 + string-length($pArg2)"/>
 </xsl:template>
</xsl:stylesheet>

عند تطبيقه على مستند XML المصدر الأصلي:

<news>
    <entry>
        <title>Lorem Ipsum</title>
        <body>
            <p>
                <strong>Lorem Ipsum</strong>
            </p>
            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed malesuada auctor magna. Vivamus urna justo, pulvinar nec, sagittis malesuada, accumsan in, massa. Quisque mi purus, gravida eget, ultricies a, porta in, sem. Maecenas justo elit, elementum vel, feugiat vulputate, pulvinar nec, velit. Fusce vel ante et diam bibendum euismod. Nunc vel nulla non lorem dignissim placerat. Nulla magna massa, auctor et, tempor nec, auctor sit amet, turpis. Quisque odio lacus, auctor at, posuere id, suscipit eget, dui. Phasellus aliquam. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Proin varius. Phasellus cursus. Cras mattis adipiscing turpis. Sed.</p>
            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed malesuada auctor magna.</p>
            <p>This text should not be displayed</p>
        </body>
    </entry>
</news>

يحقق النتيجة المطلوبة:

<news>
   <entry>
      <title>Lorem Ipsum</title>
      <body>
         <p>
            <strong>Lorem Ipsum</strong>
         </p>
         <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed malesuada auctor magna. Vivamus urna justo, pulvinar nec, sagittis malesuada, accumsan in, massa. Quisque mi purus, gravida eget, ultricies a, porta in, sem. Maecenas justo elit, elementum vel, feugiat vulputate, pulvinar nec, velit. Fusce vel ante et diam bibendum euismod. Nunc vel nulla non lorem dignissim placerat. Nulla magna massa, auctor et, tempor nec, auctor sit amet, turpis. Quisque odio lacus, auctor at, posuere id, suscipit eget, dui. Phasellus aliquam. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Proin varius. Phasellus cursus. Cras mattis adipiscing turpis. Sed.</p>
         <p>Lorem <strong> ...more</strong>
         </p>
         <p/>
      </body>
   </entry>
</news>

القيام مذكرة الأتى:

  1. ال scanl ورقة الأنماط من مكتبة FXSL يتم استيراده.يُستخدم هذا القالب بشكل شائع لتجميع البيانات من معالجة قائمة العناصر.الوظيفة (مطابقة القالب myAdd:*) الذي يقوم بالمعالجة الفعلية يتم تمريره كمعلمة إلى scanl نموذج.المعلمة الأخرى التي يجب تمريرها إليها هي القيمة "الأولية" من المعالجة، والتي سيتم إرجاعها إذا كانت قائمة العناصر التي تم تمريرها فارغة.

  2. المعلمة العالمية $pTruncateLength يحمل الحد الأقصى لطول السلسلة الذي يجب اقتطاع النص بعده

ما تطلبه هو XSLT القطع الناقص مولد كهرباء.

ربما هذا قالب xslt 1.0 قد تعطيك فكرة:

وهنا الجوهر الرئيسي لذلك:

<xsl:template match="text()" mode="label">
    <xsl:param name="self-x"/>
    <xsl:param name="self-y"/>
    <xsl:variable name="text" select="normalize-space(.)"/>
    <!-- a quick and dirty way to avoid problems with line breaks -->
    <!-- replace the select attribute with this call
         if you want to use a fancier way to escape whitespace
         characters:
          <xsl:call-template name="escape-ws"
            <xsl:with-param name="text" select="." /
          </xsl:call-template
    -->
    <use xlink:href="#text-box" transform="translate({$self-x} 
 {$self-y})"/>
    <!-- text nodes are marked with a little box -->
    <text x="{$self-x + $writing-bump-over}"
          y="{$self-y - $writing-bump-up}"
          style="{$text-font-style}; stroke:none; fill:{$text-color}">
      <xsl:text>"</xsl:text>
      <xsl:value-of select="substring($text,1,$max-text-length)"/>
      <!-- truncate the text node to $max-text-length -->
      <xsl:if test="string-length($text) &gt; $max-text-length">
        <!-- add an ellipsis if necessary -->
        <xsl:text>...</xsl:text>
      </xsl:if>
      <xsl:text>"</xsl:text>
    </text>
  </xsl:template>

ملحوظة:

  • سوف تحتاج إلى استبدال علامة الحذف برابط، ولكن الفكرة الرئيسية موجودة.
  • وهذا لا يمثل سوى مقتطف صغير من كل النص
  • قد لا تحتاج إلى كل شيء فيه:اذا احتجت "<use xlink:href="..."، أنت بحاجه إلى أعلن مساحة الاسم xlink

وبعد الكثير من القرصنة، توصلت إلى هذا الحل:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<!--
    Author: Neil Albrock
    Version: 1.0
    Description: Truncate by a character limit and retain HTML content.
    Usage: 
        <xsl:call-template name="truncate">
            <xsl:with-param name="data" select="path/to/your/body" />
            <xsl:with-param name="length" select="250" />
            <xsl:with-param name="link" select="'href'" />
        </xsl:call-template>
-->

<xsl:template name="truncate">

    <!-- The node set to be worked on. -->
    <xsl:param name="data"/>
    <!-- The desired truncate length. Default to length of data. -->
    <xsl:param name="length" select="string-length($data)"/>
    <!-- More link -->
    <xsl:param name="link"/>

    <xsl:choose>
        <!-- Return whole data if it's within length. -->
        <xsl:when test="string-length($data) &lt;= $length">
            <xsl:copy-of select="$data" />
        </xsl:when>
        <!-- Truncate to desired length. -->
        <xsl:otherwise>
            <xsl:for-each select="$data/*">
                <xsl:variable name="this-node" select="string-length(.)"/>
                <xsl:variable name="preceding-nodes">
                    <xsl:copy-of select="preceding-sibling::*"/>
                </xsl:variable>
                <xsl:variable name="node-sum" select="string-length(normalize-space($preceding-nodes))"/>
                <xsl:variable name="limit" select="$node-sum + $this-node"/>

                <xsl:choose>
                    <xsl:when test="$limit &gt; $length and $node-sum &lt;= $length">
                        <p>
                        <xsl:value-of select="substring(.,1,$length - $node-sum)"/>
                        <xsl:text>&#8230;</xsl:text>
                        <a>
                            <xsl:attribute name="href">
                                <xsl:value-of select="$link"/>
                            </xsl:attribute>
                            <xsl:text>more</xsl:text>
                        </a>
                        </p>
                    </xsl:when>
                    <xsl:when test="$limit &lt; $length">
                        <xsl:copy-of select="."/>
                    </xsl:when>
                    <xsl:otherwise/>
                </xsl:choose>

            </xsl:for-each>
        </xsl:otherwise>
    </xsl:choose>

</xsl:template>

</xsl:stylesheet>

سأستخدم الحل من خلال Chaotic Pattern على الرغم من أنه أكثر أناقة ؛-)

ستكون هذه حلقة مؤلمة باستخدام XSLT.أوصي بشدة باستخدام لغة برمجة نصية مثل Perl/Python لمحاولة ذلك.

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