递归XSLT转换

问题描述:

又是一个有问题的任务。我有一个不是很好的XML。例如以下:递归XSLT转换

<?xml version="1.0" encoding="UTF-8"?> 
<Values> 
<record name='svc_sig'> 
<record name="sig_in"> 
    <array depth="1" name="rec_fields" type="record"> 
     <record> 
      <!-- Some irrelevant metadata information with value node name... --> 
      <value name="field_name">docTest</value> 
      <value name="field_type">record</value> 
      <value name="field_dim">0</value> 
      <array depth="1" name="rec_fields" type="record"> 
       <record javaclass="com.wm.util.Values"> 
        <!-- Some irrelevant metadata information with value node name... --> 
        <value name="field_name">doc.name</value> 
        <value name="field_type">string</value> 
        <value name="field_dim">0</value> 
       </record> 
      </array> 
     </record> 
     <record> 
      <value name="field_name">docListTest</value> 
      <value name="field_type">record</value> 
      <value name="field_dim">1</value> 
      <array depth="1" name="rec_fields" type="record"> 
       <record> 
        <value name="field_name">d0</value> 
        <value name="field_type">record</value> 
        <value name="field_dim">0</value> 
        <array depth="1" name="rec_fields" type="record"> 
         <record> 
          <value name="field_name">d0.name</value> 
          <value name="field_type">string</value> 
          <value name="field_dim">0</value> 
         </record> 
        </array> 
       </record> 
      </array> 
     </record> 
     <record> 
      <value name="field_name">packages_should_work</value> 
      <value name="field_type">recref</value> 
      <value name="field_dim">0</value> 
      <value name="rec_ref">data:packages</value> 
     </record> 
     <record> 
      <value name="field_name">packages_list_should_work</value> 
      <value name="field_type">recref</value> 
      <value name="field_dim">1</value> 
      <value name="rec_ref">data:packages</value> 
     </record> 
    </array> 
</record> 
</record> 
</Values> 

为了简单起见,我需要映射此XML为已给出一个java类,我不能改变它。考虑到这一点,我必须将这个XML转换成另一个有意义的名称。例如:

<sig_in> 
    <record> 
     <field_name>docTest</field_name> 
     <field_type>record</field_type> 
     <field_dim>0</field_dim> 
    </record> 
    <record> 
     <field_name>docListTest</field_name> 
     <field_type>record</field_type> 
     <field_dim>1</field_dim> 
    </record> 
    <record> 
     <field_name>packages_should_work</field_name> 
     <field_type>recref</field_type> 
     <field_dim>0</field_dim> 
    </record> 
    <record> 
     <field_name>packages_list_should_work</field_name> 
     <field_type>recref</field_type> 
     <field_dim>1</field_dim> 
    </record> 
</sig_in> 

到目前为止,我创造了这样的事情:

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> 
<xsl:output method="xml" indent="yes" encoding="UTF-8" /> 
    <xsl:template match="/" name="service_signature"> 
     <sig_in> 
      <xsl:for-each select="Values/record[@name='svc_sig']/record[@name='sig_in']/array[@name]/record"> 
       <record> 
        <field_name><xsl:value-of select="value[@name='field_name']/text()"/></field_name> 
        <field_type><xsl:value-of select="value[@name='field_type']/text()"/></field_type> 
        <field_dim><xsl:value-of select="value[@name='field_dim']/text()"/></field_dim> 
       </record> 
      </xsl:for-each> 
     </sig_in> 
    </xsl:template> 

</xsl:stylesheet> 

虽然作品为主要内容将不会为嵌套的记录工作。我可以创建一个xsl:for-each并遍历每个项目,如果该类型是记录的,但那不会解决太多;因为它可以是任何深度的。我知道我应该使用递归我只是不能想象我怎么能在这种特殊情况下做到这一点。

@Edit - 一些修正,以嵌套类型:

<record> 
    <field_name>docListTree</field_name> 
    <field_type>record</field_type> 
    <field_dim>1</field_dim> 
    <record> 
    <field_name>d0</field_name> 
    <field_type>record</field_type> 
    <field_dim>0</field_dim> 
    <record> 
     <field_name>d0.name</field_name> 
     <field_type>string</field_type> 
     <field_dim>0</field_dim> 
    </record> 
    </record> 
</record> 

因此,大家可以看到原始类型嵌套我需要在生成的XML也同样如此。或者在一个单位中,我需要在父母和子节点中使用一些唯一的标识符,所以我知道哪一个包含哪个。无论如何,我不应该放松这个结构。

@编辑︰ - 真的很抱歉,我想节省空间,我没有显示墙背后的复杂性。所以每个记录都包含值名称节点。他们中的大多数只包含我不需要的无用的元数据信息。还有两个记录,它们是@name {sig_in,sig_out},我只需要sig_in,field_name,field_type,field_dim信息,以及嵌套记录的相同方式。我会查看所有推荐的选项,并尝试修改它们以符合需要。

感谢您的帮助! - 乔

+0

只有这个清楚。输出应该包含输入的任何记录,而不考虑任何条件? – 2013-05-07 07:39:20

+0

结构应该保持不变。如果一个记录嵌套在另一个记录中,它应该嵌套在结果xml中。 – Wrath 2013-05-07 07:48:48

尝试是这样的:

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

    <xsl:template match="value[@name]"> 
     <xsl:element name="{@name}"> 
      <xsl:value-of select="text()"/> 
     </xsl:element> 

    </xsl:template> 

    <xsl:template match="record"> 
      <xsl:copy> 
       <xsl:apply-templates /> 
      </xsl:copy> 
    </xsl:template> 
    <xsl:template match="/*"> 
     <xsl:apply-templates /> 
    </xsl:template> 

    <xsl:template match="node()"> 
     <xsl:apply-templates select="node()" /> 
    </xsl:template> 
</xsl:stylesheet> 

将产生以下的输出:

<?xml version="1.0"?> 
<record> 
    <field_name>docTest</field_name> 
    <field_type>record</field_type> 
    <field_dim>0</field_dim> 
    <record> 
    <field_name>doc.name</field_name> 
    <field_type>string</field_type> 
    <field_dim>0</field_dim> 
    </record> 
</record><record> 
    <field_name>docListTest</field_name> 
    <field_type>record</field_type> 
    <field_dim>1</field_dim> 
    <record> 
    <field_name>d0</field_name> 
    <field_type>record</field_type> 
    <field_dim>0</field_dim> 
    <record> 
     <field_name>d0.name</field_name> 
     <field_type>string</field_type> 
     <field_dim>0</field_dim> 
    </record> 
    </record> 
</record><record> 
    <field_name>packages_should_work</field_name> 
    <field_type>recref</field_type> 
    <field_dim>0</field_dim> 
    <rec_ref>data:packages</rec_ref> 
</record><record> 
    <field_name>packages_list_should_work</field_name> 
    <field_type>recref</field_type> 
    <field_dim>1</field_dim> 
    <rec_ref>data:packages</rec_ref> 
</record> 

更新能源部更多信息问题:

你可以轻易地在记录或值模板的一些条件。 这就是我所理解的:只考虑具有@name值的记录是“sig_in”。
试试这个:

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

    <xsl:template match="value[@name = 'field_name' or 
          @name = 'field_type' or 
          @name = 'field_dim']"> 
     <xsl:element name="{@name}"> 
      <xsl:value-of select="text()"/> 
     </xsl:element> 

    </xsl:template> 

    <xsl:template match="record[descendant-or-self::record[@name='sig_in'] or 
        ancestor::record[@name='sig_in']]"> 
      <xsl:copy> 
       <xsl:apply-templates /> 
      </xsl:copy> 
    </xsl:template> 


    <xsl:template match="/*"> 
     <xsl:apply-templates /> 
    </xsl:template> 

    <xsl:template match="node()"> 
     <xsl:apply-templates select="node()" /> 
    </xsl:template> 
</xsl:stylesheet> 
+0

看起来很有前途,但它适合每个记录或价值节点,但我只需要特定的节点。这再次是我的错误,我试图节省大量的空间,而不是粘贴整个大型的XML。我会更新我的原始xml示例。 – Wrath 2013-05-07 08:20:00

+0

@Joey:你看过更新吗?这只考虑'@ name ='sig_in @ name ='sig_in'作为父母或孩子(任何深度)的记录。 – 2013-05-07 20:31:03

这XSLT是能够生成您发布结果:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> 
    <xsl:output method="xml" indent="yes" encoding="UTF-8" /> 
    <xsl:template match="/" name="service_signature"> 
    <sig_in> 
     <xsl:for-each select="/record[@name='sig_in']/array[@name]/record"> 
     <record> 
      <field_name><xsl:value-of select="value[@name='field_name']/text()"/></field_name> 
      <field_type><xsl:value-of select="value[@name='field_type']/text()"/></field_type> 
      <field_dim><xsl:value-of select="value[@name='field_dim']/text()"/></field_dim> 
     </record> 
     </xsl:for-each> 
    </sig_in> 
    </xsl:template> 

</xsl:stylesheet> 

但是,如果你想遍历每个记录使用:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> 
    <xsl:output method="xml" indent="yes" encoding="UTF-8" /> 
    <xsl:template match="/" name="service_signature"> 
    <sig_in> 
     <xsl:for-each select="//array[@name]/record"> 
     <record> 
      <field_name><xsl:value-of select="value[@name='field_name']/text()"/></field_name> 
      <field_type><xsl:value-of select="value[@name='field_type']/text()"/></field_type> 
      <field_dim><xsl:value-of select="value[@name='field_dim']/text()"/></field_dim> 
     </record> 
     </xsl:for-each> 
    </sig_in> 
    </xsl:template> 

</xsl:stylesheet> 

这里是一个XSLT是可以帮助你。

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> 
    <xsl:strip-space elements="*"/> 
    <xsl:output indent="yes"/> 
    <xsl:template match="*"> 
     <xsl:apply-templates/> 
    </xsl:template> 
    <xsl:template match="record|value"> 
     <xsl:choose> 
      <xsl:when test="@name"> 
       <xsl:element name="{@name}"> 
        <xsl:apply-templates/> 
       </xsl:element>    
      </xsl:when> 
      <xsl:otherwise> 
       <xsl:copy> 
        <xsl:apply-templates/> 
       </xsl:copy>    
      </xsl:otherwise> 
     </xsl:choose> 
    </xsl:template> 
</xsl:stylesheet> 

有了您的XML:

<record name="sig_in"> 
    <array depth="1" name="rec_fields" type="record"> 
     <record> 
      <value name="field_name">docTest</value> 
      <value name="field_type">record</value> 
      <value name="field_dim">0</value> 
      <array depth="1" name="rec_fields" type="record"> 
       <record javaclass="com.wm.util.Values"> 
        <value name="field_name">doc.name</value> 
        <value name="field_type">string</value> 
        <value name="field_dim">0</value> 
       </record> 
      </array> 
     </record> 
     <record> 
      <value name="field_name">docListTest</value> 
      <value name="field_type">record</value> 
      <value name="field_dim">1</value> 
      <array depth="1" name="rec_fields" type="record"> 
       <record> 
        <value name="field_name">d0</value> 
        <value name="field_type">record</value> 
        <value name="field_dim">0</value> 
        <array depth="1" name="rec_fields" type="record"> 
         <record> 
          <value name="field_name">d0.name</value> 
          <value name="field_type">string</value> 
          <value name="field_dim">0</value> 
         </record> 
        </array> 
       </record> 
      </array> 
     </record> 
     <record> 
      <value name="field_name">packages_should_work</value> 
      <value name="field_type">recref</value> 
      <value name="field_dim">0</value> 
      <value name="rec_ref">data:packages</value> 
     </record> 
     <record> 
      <value name="field_name">packages_list_should_work</value> 
      <value name="field_type">recref</value> 
      <value name="field_dim">1</value> 
      <value name="rec_ref">data:packages</value> 
     </record> 
    </array> 
</record> 

结果是:

<?xml version="1.0" encoding="utf-8"?> 
<sig_in> 
    <record> 
     <field_name>docTest</field_name> 
     <field_type>record</field_type> 
     <field_dim>0</field_dim> 
     <record> 
     <field_name>doc.name</field_name> 
     <field_type>string</field_type> 
     <field_dim>0</field_dim> 
     </record> 
    </record> 
    <record> 
     <field_name>docListTest</field_name> 
     <field_type>record</field_type> 
     <field_dim>1</field_dim> 
     <record> 
     <field_name>d0</field_name> 
     <field_type>record</field_type> 
     <field_dim>0</field_dim> 
     <record> 
      <field_name>d0.name</field_name> 
      <field_type>string</field_type> 
      <field_dim>0</field_dim> 
     </record> 
     </record> 
    </record> 
    <record> 
     <field_name>packages_should_work</field_name> 
     <field_type>recref</field_type> 
     <field_dim>0</field_dim> 
     <rec_ref>data:packages</rec_ref> 
    </record> 
    <record> 
     <field_name>packages_list_should_work</field_name> 
     <field_type>recref</field_type> 
     <field_dim>1</field_dim> 
     <rec_ref>data:packages</rec_ref> 
    </record> 
</sig_in>