【问题标题】:Using XSL to move from one node to another使用 XSL 从一个节点移动到另一个节点
【发布时间】:2011-08-26 16:50:34
【问题描述】:

所以我想使用 XSL 转换以下内容

<doc>
    <data id="priority" level="2" include="true">
        <name>Priority</name>
    </data>
    <data id="cost" level="1" leveltype="number">
        <name>Cost</name>
    </data>
    <data id="date" level="3" include="true">
        <name>Date</name>
    </data>
</doc>

到这里

<doc>
    <data id="priority">
        <name>Priority</name>
    </data>
    <data id="cost">
        <name>Cost</name>
    </data>
    <data id="date">
        <name>Date</name>
    </data>

    <!-- ordering matters, though if necessary I can reorder this manually via the DOM instead of XSL -->
    <levels>   
        <level id="cost" include="false" type="number"/>
        <level id="priority" include="true"/>
        <level id="date" include="true"/>
    </level>
</doc>

基本上我想把关卡属性做成自己的东西。如果有某种方法可以删除级别编号并使用节点的顺序来表示它,那将是一个巨大的好处。

【问题讨论】:

  • 如果您可以添加您所拥有的样本以及最终结果的样本,那将有助于我们为您提供帮助。
  • 好问题,+1。有关完整、简短且仅基于模板的解决方案,请参阅我的答案——这可能是所有答案中最短、最简单且最容易扩展的。

标签: xml xslt dom xpath


【解决方案1】:

解决办法如下:

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

  <!-- attribute suppression template -->
  <xsl:template match="@*" priority="2"/>

  <xsl:template match="/doc">
    <xsl:copy>
      <xsl:apply-templates select="*" mode="data"/>
      <levels>
        <xsl:apply-templates select="*" mode="levels">
          <xsl:sort select="@level" data-type="number"/>
        </xsl:apply-templates>
      </levels>
    </xsl:copy>
  </xsl:template>


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

  <xsl:template match="@*" mode="data"/><!-- suppress -->
  <xsl:template match="@id" mode="data" priority="2"><!-- keep -->
    <xsl:copy-of select="."/></xsl:template>


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

  <xsl:template match="data" mode="levels">
    <level>
      <xsl:apply-templates select="@*" mode="levels"/>
    </level></xsl:template>

  <xsl:template match="@level" mode="levels"/><!-- suppress -->
  <xsl:template match="@leveltype" mode="levels"><!-- rename -->
    <xsl:attribute name="type"><xsl:value-of select="."/>
    </xsl:attribute></xsl:template>

</xsl:stylesheet>

我假设您预期输出中的 &lt;level id="cost" include="false" type="number"/&gt; 是复制/粘贴伪影,因为输入中的 level[@id="cost"] 缺少该属性。

【讨论】:

    【解决方案2】:

    可能是更简单的方法:

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
        <doc>
            <xsl:for-each select="doc/data">
                <data>
                    <xsl:attribute name="id">
                        <xsl:value-of select="@id"/>
                    </xsl:attribute>
                    <name><xsl:value-of select="name" /></name>
                </data>
            </xsl:for-each>
            <levels>
                <xsl:for-each select="doc/data">
                    <xsl:sort select="@level" />
                    <level>
                        <xsl:attribute name="id">
                            <xsl:value-of select="@id"/>
                        </xsl:attribute>
                        <xsl:choose>
                            <xsl:when test="@include='true'">
                                <xsl:attribute name="include">true</xsl:attribute>
                            </xsl:when>
                            <xsl:otherwise>
                                <xsl:attribute name="include">false</xsl:attribute>
                            </xsl:otherwise>
                        </xsl:choose>
                    </level>
                </xsl:for-each>
            </levels>
        </doc>
    </xsl:template>
    </xsl:stylesheet>
    

    【讨论】:

      【解决方案3】:

      只是一个变体:

      <xsl:stylesheet version="1.0"
       xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
       <xsl:output omit-xml-declaration="yes" indent="yes"/>
      
      <xsl:template match="doc">
      
       <doc>
      
        <!-- build and sort data nodes -->
        <xsl:for-each select="data">
         <xsl:sort select="@id"/>
          <data id="{@id}">
           <xsl:copy-of select="name"/>
          </data>
         </xsl:for-each>
      
         <!-- build and sort levels -->
         <levels>
          <xsl:for-each select="data">
           <xsl:sort select="@id"/>
            <level id="{@id}" include="{boolean(@include)}">
             <xsl:if test="@leveltype">
              <xsl:attribute name="type">
               <xsl:value-of select="@leveltype"/>
              </xsl:attribute>
             </xsl:if>
            </level>
          </xsl:for-each>
         </levels>
      
       </doc>
      
       </xsl:template>
      </xsl:stylesheet>
      

      【讨论】:

        【解决方案4】:

        这是一个更短更简单的解决方案,只使用模板(没有&lt;xsl:for-each&gt;):

        <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:copy>
               <xsl:apply-templates select="node()|@*"/>
             </xsl:copy>
         </xsl:template>
        
         <xsl:template match="/*">
          <doc>
           <xsl:apply-templates select="*"/>
           <levels>
            <xsl:apply-templates select="data" mode="level">
             <xsl:sort select="@level" data-type="number"/>
            </xsl:apply-templates>
           </levels>
          </doc>
         </xsl:template>
         <xsl:template match="data/@*[not(name()='id')]"/>
        
         <xsl:template match="data" mode="level">
          <level id="{@id}" include="{boolean(@include)}">
           <xsl:if test="@leveltype">
            <xsl:attribute name="type"><xsl:value-of select="@leveltype"/></xsl:attribute>
           </xsl:if>
          </level>
         </xsl:template>
        </xsl:stylesheet>
        

        应用于提供的 XML 文档时

        <doc>
            <data id="priority" level="2" include="true">
                <name>Priority</name>
            </data>
            <data id="cost" level="1" leveltype="number">
                <name>Cost</name>
            </data>
            <data id="date" level="3" include="true">
                <name>Date</name>
            </data>
        </doc>
        

        产生想要的正确结果

        <doc>
           <data id="priority">
              <name>Priority</name>
           </data>
           <data id="cost">
              <name>Cost</name>
           </data>
           <data id="date">
              <name>Date</name>
           </data>
           <levels>
              <level id="cost" include="false" type="number"/>
              <level id="priority" include="true"/>
              <level id="date" include="true"/>
           </levels>
        </doc>
        

        解释

        1. 使用和覆盖身份规则/模板。

        2. 使用mode="level" 生成结果文档的第二部分。

        【讨论】:

        • +1。我已经徘徊了一段时间没有xsl:for-each的解决方案。
        • @empo:谢谢:我使用&lt;xsl:for-each&gt;(几乎)仅在需要将当前文档更改为另一个文档以便key() 函数可以与第二个文档一起使用的情况下.除了这个用例,我认为没有其他情况需要&lt;xsl:for-each&gt;
        • 听起来很简单!下次我会考虑使用循环。
        • @ToddMyhre:是的,mode 属性在这种情况下非常有用。
        猜你喜欢
        • 2015-03-21
        • 2019-12-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-03-20
        • 2023-03-10
        • 2012-03-12
        • 2020-06-10
        相关资源
        最近更新 更多