1

更新:我感谢所有的帮助。在你们的帮助下,我能够让 Process elem 标签工作,但我的老板有一个新要求。我必须将元素移动到一个新位置(而不是删除它们)并正确放置元素编号。再次感谢你们,你们是最棒的!

上一个问题:

我是 xslt 的新手,所以请多多包涵。所以这就是问题所在。我有这个xml:

 <Process>
          <elem0>
             <pcode>xx<pcode>
          </elem0>
          <elem1>
             <pcode>xy<pcode>
          </elem1>
          <elem2>
             <pcode>ab<pcode>
          </elem2>
          <elem3>
             <pcode>AD<pcode>
          </elem3>               
    </Process>

我必须用 pcode value='xy' 擦除元素,我用 xslt 成功地做到了这一点。但是,在这样做之后,您会看到元素 (elem0 elem2 elem3) 名称之间存在间隙。

<Process>
      <elem0>
         <pcode>xx<pcode>
      </elem0>
      <elem2>
         <pcode>ab<pcode>
      </elem2>
      <elem3>
         <pcode>AD<pcode>
      </elem3>               
</Process>

我希望它是

<Process>
          <elem0>
             <pcode>xx<pcode>
          </elem0>
          <elem1>
             <pcode>ab<pcode>
          </elem1>
          <elem2>
             <pcode>AD<pcode>
          </elem2>               
    </Process> so it shows up properly in front end, but I am stuck. Tried sorting but didn't work. elem identifications are

改变,所以我更难使用某种模板。谢谢指教!

更新:新要求将某些元素移动到新位置,而不是删除它们。对于给您带来的不便,我深表歉意,并提前感谢您的所有帮助。

我有这个xml:

 <Process>
          <elem0>
             <pcode>xx<pcode>
          </elem0>
          <elem1>
             <pcode>xy<pcode>
          </elem1>
          <elem2>
             <pcode>ab<pcode>
          </elem2>
          <elem3>
             <pcode>AD<pcode>
          </elem3>               
    </Process>

而且我必须将 pcode value='xy' 的元素移动(擦除旧要求)到 EdProcess,我使用 xslt 成功完成了它。但是,在这样做之后,您会看到元素 (elem0 elem2 elem3) 名称之间存在间隙。此外,EdProcess 需要从 elem0 开始,任何被移动的新元素都应该按顺序排列,即 elem0、elem1、elem2 等。

<Process>
      <elem0>
         <pcode>xx<pcode>
      </elem0>
      <elem2>
         <pcode>ab<pcode>
      </elem2>
      <elem3>
         <pcode>AD<pcode>
      </elem3>               
</Process>



<EdProcess>
          <elem1>
             <pcode>xy<pcode>
          </elem1>
   </EdProcess>

我希望它是

<Process>
          <elem0>
             <pcode>xx<pcode>
          </elem0>
          <elem1>
             <pcode>ab<pcode>
          </elem1>
          <elem2>
             <pcode>AD<pcode>
          </elem2>               
    </Process>

 <EdProcess>
          <elem0>
             <pcode>xy<pcode>
          </elem0>
   </EdProcess>

所以它在前端正确显示,但我被卡住了。尝试排序但没有用。elem 标识正在发生变化,因此我很难使用某种模板。谢谢指教!

4

5 回答 5

2

假设您的源格式没有与elems 混合的其他元素,有一种非常简单直接的方法可以做到这一点,不需要前置/后置兄弟或双通道:

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

  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="/*">
    <xsl:copy>
      <xsl:apply-templates select="*[pcode != 'xy']" mode="elems" />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="*" mode="elems">
    <xsl:element name="elem{position() - 1}">
      <xsl:apply-templates select="@* | node()" />
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>

应用于此输入时:

<Process>
  <elem0>
    <pcode>xx</pcode>
  </elem0>
  <elem1>
    <pcode>xy</pcode>
  </elem1>
  <elem2>
    <pcode>ab</pcode>
  </elem2>
  <elem3>
    <pcode>xy</pcode>
  </elem3>
  <elem4>
    <pcode>AD</pcode>
  </elem4>
</Process>

产生:

<Process>
  <elem0>
    <pcode>xx</pcode>
  </elem0>
  <elem1>
    <pcode>ab</pcode>
  </elem1>
  <elem2>
    <pcode>AD</pcode>
  </elem2>
</Process>
于 2013-03-12T17:01:15.840 回答
0

由于 XSLT 不做可更新变量,我能看到的最直接的方法是一个递归模板,一次处理一个兄弟

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:template match="/">
    <Process>
      <!-- start with the first child -->
      <xsl:apply-templates select="Process/*[pcode != 'xy'][1]" />
    </Process>
  </xsl:template>

  <xsl:template match="*">
    <xsl:param name="nextIndex" select="0" />
    <xsl:element name="elem{$nextIndex}">
      <xsl:copy-of select="node()" />
    </xsl:element>
    <!-- process the next applicable sibling, if any, incrementing the index -->
    <xsl:apply-templates select="following-sibling::*[pcode != 'xy'][1]">
      <xsl:with-param name="nextIndex" select="$nextIndex + 1" />
    </xsl:apply-templates>
  </xsl:template>
</xsl:stylesheet>
于 2013-03-12T16:31:59.247 回答
0

您可能需要使用两遍 XSLT 模式,并重命名第二遍中的所有元素:

<xsl:template match="/">
    <xsl:variable name="pass1">
      <xsl:apply-templates select="Process"/>
    </xsl:variable>
    <xsl:apply-templates select="exsl:node-set($pass1)" mode="pass2" />
<xsl:template>

<xsl:template match="Process" mode="pass2">
  <xsl:copy>
    <xsl:for-each select="*">
       <xsl:element name="{ concat('elem', position()) }">
          <xsl:apply-templates />
       </xsl:element>
    </xsl:for-each>
  </xsl:copy>
</xsl:template>

请注意,对于 XSLT 1,您需要使用node-set()大多数 XSLT 1 处理器中包含的函数,但您需要在样式表的顶部包含名称空间声明。对于 ESLT 感知处理器(Saxon、xsltproc、Xalan-J、jd.xslt 和 4XSLT),使用xmlns:exsl="http://exslt.org/common"exsl:node-set(). 对于 Xalan-C 使用xmlns:xalan="http://xml.apache.org/xalan"xalan:nodeset(). 对于 MSXSL 使用xmlns:msxsl="urn:schemas-microsoft-com:xslt"msxsl:node-set().

于 2013-03-12T16:34:56.960 回答
0

您可以使用preceding-sibling轴来计算出现当前elem*元素之前的元素数量。然后减去将被删除的数字,就得到了当前的数字:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="Process/*">
        <!-- only copy if not "xy" -->
        <xsl:if test="pcode!='xy'">
            <!-- number of preceding elements which are also not "xy" -->
            <xsl:element name="elem{count(preceding-sibling::*[pcode!='xy'])}">
                <xsl:apply-templates select="@*|node()"/>
            </xsl:element>
        </xsl:if>
    </xsl:template>

</xsl:stylesheet>

也就是说,这是一种糟糕的 XML 格式,如果可以的话,您应该更改这种格式,这样它就不会有未知的元素名称,例如elem0. 如果您需要知道一个元素在列表中的位置,您可以使用 、 等轻松获得它position()count(preceding-sibling::*)如果您确实需要一个明确的数字,请将其放在属性中,而不是元素名称中,例如<elem num="1">

于 2013-03-12T16:46:15.880 回答
0

这是一个一次性解决方案,可以正确处理elemN 个元素,这些元素可以位于树中的任何位置——不仅仅是兄弟姐妹。其他答案均未正确处理此类 XML 文档。

它相当有效——在树的深度有限的情况下,它的效率接近线性 (O(N))。如果所有这些元素都是兄弟元素,则它线性的

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:template match="node()|@*">
  <xsl:param name="pCount" select="0"/>
  <xsl:copy>
   <xsl:apply-templates select="node()[1]|@*">
     <xsl:with-param name="pCount" select="$pCount"/>
   </xsl:apply-templates>
  </xsl:copy>
  <xsl:apply-templates select="following-sibling::node()[1]">
     <xsl:with-param name="pCount" select=
      "$pCount + count(.//*[starts-with(name(), 'elem') and not(pcode='xy')])"/>
  </xsl:apply-templates>
 </xsl:template>

 <xsl:template match="*[starts-with(name(), 'elem') and not(pcode='xy')]">
  <xsl:param name="pCount" select="0"/>
  <xsl:element name="elem{$pCount}">
   <xsl:apply-templates select="node()[1]|@*">
     <xsl:with-param name="pCount" select="$pCount+1"/>
   </xsl:apply-templates>
  </xsl:element>
  <xsl:apply-templates select="following-sibling::node()[1]">
     <xsl:with-param name="pCount" select=
     "$pCount+1+ count(.//*[starts-with(name(), 'elem') and not(pcode='xy')])"/>
  </xsl:apply-templates>
 </xsl:template>

 <xsl:template match="*[starts-with(name(), 'elem') and pcode='xy']">
  <xsl:param name="pCount" select="0"/>

  <xsl:apply-templates select="following-sibling::node()[1]">
     <xsl:with-param name="pCount" select="$pCount"/>
  </xsl:apply-templates>
 </xsl:template>
</xsl:stylesheet>

当此转换应用于以下 XML 文档时:

<Process>
  <a/>
  <elem0>
    <pcode>xx</pcode>
      <elem1>
        <pcode>xy</pcode>
      </elem1>
      <elem2>
        <pcode>xz</pcode>
      </elem2>
 </elem0>
  <b/>
  <elem3>
    <pcode>xy</pcode>
  </elem3>
  <c/>
  <elem4>
    <pcode>ab</pcode>
  </elem4>
  <d/>
  <elem5>
    <pcode>xy</pcode>
  </elem5>
  <e/>
  <elem6>
    <pcode>AD</pcode>
  </elem6>
  <f/>
</Process>

产生了想要的正确结果:

<Process>
   <a/>
   <elem0>
      <pcode>xx</pcode>
      <elem1>
         <pcode>xz</pcode>
      </elem1>
   </elem0>
   <b/>
   <c/>
   <elem2>
      <pcode>ab</pcode>
   </elem2>
   <d/>
   <e/>
   <elem3>
      <pcode>AD</pcode>
   </elem3>
   <f/>
</Process>
于 2013-03-13T03:26:06.483 回答