使用XSL从一个节点移动到另一个节点

时间:2021-12-24 20:29:20

So I want to convert the following using XSL

所以我想使用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>

To this

对此

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

Basically I want to take the level attributes and make them their own thing. A huge bonus would be if there were some way to remove the level number and use the order of the node instead to represent that.

基本上我想采用关卡属性并使它们成为自己的东西。如果有某种方法可以删除级别编号并使用节点的顺序代替它,那么将会有巨大的好处。

4 个解决方案

#1


3  

This is a shorter and simpler solution using only templates (no <xsl:for-each>):

这是一个更简单,更简单的解决方案,仅使用模板(没有 ): :for-each>

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

When applied on the provided XML document:

应用于提供的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>

the wanted, correct result is produced:

产生了想要的正确结果:

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

Explanation:

说明:

  1. Using and overriding the identity rule/template.

    使用和覆盖标识规则/模板。

  2. Using mode="level" to generate the second part of the result-document.

    使用mode =“level”生成结果文档的第二部分。

#2


2  

Just a variant:

只是一个变种:

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

#3


0  

Here's the solution:

这是解决方案:

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

I assume that <level id="cost" include="false" type="number"/> in your expected output is a copy/paste artefact as the attribute is missing from level[@id="cost"] in the input.

我假设您的预期输出中的 是一个复制/粘贴工件,因为输入中的级别[@ id =“cost”]缺少该属性。

#4


0  

May be a simpler way:

可能是一种更简单的方法:

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

#1


3  

This is a shorter and simpler solution using only templates (no <xsl:for-each>):

这是一个更简单,更简单的解决方案,仅使用模板(没有 ): :for-each>

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

When applied on the provided XML document:

应用于提供的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>

the wanted, correct result is produced:

产生了想要的正确结果:

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

Explanation:

说明:

  1. Using and overriding the identity rule/template.

    使用和覆盖标识规则/模板。

  2. Using mode="level" to generate the second part of the result-document.

    使用mode =“level”生成结果文档的第二部分。

#2


2  

Just a variant:

只是一个变种:

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

#3


0  

Here's the solution:

这是解决方案:

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

I assume that <level id="cost" include="false" type="number"/> in your expected output is a copy/paste artefact as the attribute is missing from level[@id="cost"] in the input.

我假设您的预期输出中的 是一个复制/粘贴工件,因为输入中的级别[@ id =“cost”]缺少该属性。

#4


0  

May be a simpler way:

可能是一种更简单的方法:

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