0

我是 XSLT 转换的新手,这是我整个晚上都在尝试做的事情,我希望能得到你的帮助:我有一个这样的 XML 结构:

   <DEV>
       <HDR>
           <ApID>value1</ApID>
           <STAT>value2</STAT>
       </HDR>
       <DSC>
           <Cap>value3</Cap>
       </DSC>
   </DEV>

基本上我需要将没有子元素的所有元素值转换为名称为“V”的属性:

<DEV>
    <HDR>
        <ApID V= "value1" />
        <STAT V= "value2" />
    </HDR>
    <DSC>
        <Cap V = "value3" />
    </DSC>
</DEV>

有没有办法用 XSLT 编写像这样的通用转换?

4

2 回答 2

2

XSLT 允许您递归地遍历节点树。在遍历您的树时,您可以通过创建比其他模板规则更具体的模板规则来让 XSLT 处理您的节点。

您要解决的问题是在转换您的 XML 树时有一些细微的差别。一个好的开始是进行身份转换

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

此身份转换将简单地生成与输入 XML 相同的 XML 输出。它按原样匹配并复制每个 XML 节点。然后你一步一步地塑造你的输出,直到你得到你想要的。请参阅<xsl:copy/>.

您要进行的此复制的例外情况是遇到没有任何子节点的元素时。为了匹配任何元素,我们使用*. 为了不匹配任何元素,我们使用not(*). 为了匹配没有元素的任何元素,我们因此使用*[not(*)]. 事实上,这些规则是由XSLT 使用的XPath查询语言定义的。让我们针对您的 XML 尝试以下 XSLT:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">
    <xsl:template match="*[not(*)]">
        <found-an-element-with-no-children/>
    </xsl:template>
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

我们得到:

<?xml version="1.0" encoding="utf-8"?><DEV>
    <HDR>
        <found-an-element-with-no-children/>
        <found-an-element-with-no-children/>
    </HDR>
    <DSC>
        <found-an-element-with-no-children/>
    </DSC>
</DEV>

好处是任何一个节点一次只能匹配一个模板规则。我们看起来更接近我们现在想要的位置。

这就是<xsl:copy/>进来的地方。我们现在将使用它,因为我们想要复制原始元素的名称。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">
    <xsl:template match="*[not(*)]">
        <xsl:copy>
            <found-an-element-with-no-children/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

现在我们得到:

<?xml version="1.0" encoding="utf-8"?><DEV>
    <HDR>
        <ApID><found-an-element-with-no-children/></ApID>
        <STAT><found-an-element-with-no-children/></STAT>
    </HDR>
    <DSC>
        <Cap><found-an-element-with-no-children/></Cap>
    </DSC>
</DEV

现在为我们新创建的节点添加一个属性,并在其中包含我们刚才匹配的节点的文本内容:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">
    <xsl:template match="*[not(*)]">
        <xsl:copy>
            <xsl:attribute name="V">
                <xsl:value-of select="."/>
            </xsl:attribute>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

看起来我们在这里:

<?xml version="1.0" encoding="utf-8"?><DEV>
    <HDR>
        <ApID V="value1"/>
        <STAT V="value2"/>
    </HDR>
    <DSC>
        <Cap V="value3"/>
    </DSC>
</DEV>

现在有几个指针:

  1. 您的 , 都没有ApIDSTAT并且Cap将能够复制其旧属性。您将希望<xsl:apply-templates select="@*"/>包含这些属性。
  2. 当你包含这些属性时,如果原始输入包含一个V属性会发生什么?
  3. 您可能还需要考虑当您匹配的标签没有文本时会发生什么 - 以及原因。
于 2013-10-14T22:16:27.790 回答
1

答案是“是”!首先,您将 XSLT 基于XSLT 身份转换。就其本身而言,这会复制 XML 中的所有节点和属性

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

通过使用它,您只需要编写模板来匹配您希望转换的节点。在你的情况下,你说你想匹配没有子元素的元素(虽然严格来说,元素确实有子元素。它们有文本节点作为子元素!但我猜你的意思是它们没有其他元素作为子元素)。所以,你的模板匹配看起来像这样

<xsl:template match="*[not(*)]">

*用于匹配元素的符号在哪里。

然后,在此模板中,您可以复制元素,并使用xsl:attribute将文本转换为属性

     <xsl:attribute name="V">
        <xsl:value-of select="text()" />
     </xsl:attribute>

试试这个 XSLT

<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="*[not(*)]">
      <xsl:copy>
         <xsl:attribute name="V">
            <xsl:value-of select="text()" />
         </xsl:attribute>
         <xsl:apply-templates select="@*" />
      </xsl:copy>
   </xsl:template>
</xsl:stylesheet>

当应用于您的 XML 时,将输出以下内容

<DEV>
  <HDR>
    <ApID V="value1" />
    <STAT V="value2" />
  </HDR>
  <DSC>
   <Cap V="value3" />
  </DSC>
</DEV>
于 2013-10-14T21:48:31.123 回答