2

XML:

<t>
  <ScreenSize>
    <Width>1440</Width>
    <Height>900</Height>
  </ScreenSize>
  <ConfigurationHotSpots>
    <Rectangle>
      <Location>
        <X>0</X>
        <Y>0</Y>
      </Location>
      <Size>
        <Width>50</Width>
        <Height>50</Height>
      </Size>
      <X>0</X>
      <Y>0</Y>
      <Width>50</Width>
      <Height>50</Height>
    </Rectangle>
  </ConfigurationHotSpots>
</t>

所需的输出 XML:

<t>
  <ScreenSizeWidth>1440</ScreenSizeWidth>
  <ScreenSizeWidth>900</ScreenSizeWidth>
  <ConfigurationHotSpotsRectangleLocationX>0</ConfigurationHotSpotsRectangleLocationX>
  <ConfigurationHotSpotsRectangleLocationY>0</ConfigurationHotSpotsRectangleLocationY>
  <ConfigurationHotSpotsRectangleSizeWidth>50</ConfigurationHotSpotsRectangleSizeWidth>
  <ConfigurationHotSpotsRectangleSizeHeight>50</ConfigurationHotSpotsRectangleSizeHeight>
  <ConfigurationHotSpotsRectangleX>0</ConfigurationHotSpotsRectangleX>
  <ConfigurationHotSpotsRectangleY>0</ConfigurationHotSpotsRectangleY>
  <ConfigurationHotSpotsRectangleWidth>50</ConfigurationHotSpotsRectangleWidth>
  <ConfigurationHotSpotsRectangleHeight>50</ConfigurationHotSpotsRectangleHeight>
</t>

规则:

  • 对于已定义节点集中的每个元素(在本例中<ScreenSize> | <ConfigurationHotSpots>),请执行以下操作: 处理所有叶后代(即没有子节点的那些),以便创建一个新元素;这个新元素的名称应该是当前节点和无子后代之间所有元素的串联。
  • 整个文档中这些“块”的数量是可变的,因此没有手动模板(即,一个只处理 的后代<ScreenSize>,一个只处理 的后代<ConfigurationHotSpots>,等等)

我目前拥有的:

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

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

  <xsl:template match="ScreenSize|ConfigurationHotSpots">
    <xsl:apply-templates select="descendant::*[not(*)]" mode="descendants" />
  </xsl:template>

  <xsl:template match="*" mode="descendants">
    <xsl:element name="{concat(name(ancestor::*[not(self::t)]), name())}">
      <xsl:apply-templates />
    </xsl:element>
  </xsl:template>

</xsl:stylesheet>

问题似乎是name(ancestor::*[not(self::t)])部分;它没有做我希望它做的事情(神奇地输出这些元素的名称,一个接一个)。相反,这就是我得到的:

<?xml version="1.0" encoding="UTF-8"?>
<t>
  <ScreenSizeWidth>1440</ScreenSizeWidth>
  <ScreenSizeHeight>900</ScreenSizeHeight>
  <ConfigurationHotSpotsX>0</ConfigurationHotSpotsX>
  <ConfigurationHotSpotsY>0</ConfigurationHotSpotsY>
  <ConfigurationHotSpotsWidth>50</ConfigurationHotSpotsWidth>
  <ConfigurationHotSpotsHeight>50</ConfigurationHotSpotsHeight>
  <ConfigurationHotSpotsX>0</ConfigurationHotSpotsX>
  <ConfigurationHotSpotsY>0</ConfigurationHotSpotsY>
  <ConfigurationHotSpotsWidth>50</ConfigurationHotSpotsWidth>
  <ConfigurationHotSpotsHeight>50</ConfigurationHotSpotsHeight>
</t>

提前致谢!

4

3 回答 3

3

Doingname(ancestor::*[not(self::t)])不会返回名称列表,而只会返回它匹配的最后一个名称的名称(或者它是第一个?)。

您可以采取一种稍微不同的方法,与您当前所做的相距不远,而不是直接跳到“叶子”元素,依次匹配每个级别,但保持元素名称的连续串联,这些名称通过通过参数从一层到另一层。

试试这个 XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes"/>
    <xsl:template match="ScreenSize|ConfigurationHotSpots">
        <xsl:apply-templates mode="descendants">
            <xsl:with-param name="name" select="local-name()" />
        </xsl:apply-templates>
    </xsl:template>

    <xsl:template match="*" mode="descendants">
        <xsl:param name="name" />
        <xsl:apply-templates mode="descendants">
            <xsl:with-param name="name" select="concat($name, local-name())" />
        </xsl:apply-templates>
    </xsl:template>

    <xsl:template match="*[not(*)]" mode="descendants">
        <xsl:param name="name" />
        <xsl:element name="{concat($name, local-name())}">
            <xsl:value-of select="." />
        </xsl:element>
    </xsl:template>

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

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

<t>
   <ScreenSizeWidth>1440</ScreenSizeWidth>
   <ScreenSizeHeight>900</ScreenSizeHeight>
   <ConfigurationHotSpotsRectangleLocationX>0</ConfigurationHotSpotsRectangleLocationX>
   <ConfigurationHotSpotsRectangleLocationY>0</ConfigurationHotSpotsRectangleLocationY>
   <ConfigurationHotSpotsRectangleSizeWidth>50</ConfigurationHotSpotsRectangleSizeWidth>
   <ConfigurationHotSpotsRectangleSizeHeight>50</ConfigurationHotSpotsRectangleSizeHeight>
   <ConfigurationHotSpotsRectangleX>0</ConfigurationHotSpotsRectangleX>
   <ConfigurationHotSpotsRectangleY>0</ConfigurationHotSpotsRectangleY>
   <ConfigurationHotSpotsRectangleWidth>50</ConfigurationHotSpotsRectangleWidth>
   <ConfigurationHotSpotsRectangleHeight>50</ConfigurationHotSpotsRectangleHeight>
</t>
于 2012-09-12T22:11:28.423 回答
2

这是可能的最短解决方案之一。它不如将累积路径作为参数传递那样有效,但对于嵌套不太深的结构,这并不重要:

<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=
 "*[not(*) and (ancestor::ScreenSize or ancestor::ConfigurationHotSpots)]">

    <xsl:variable name="vName">
        <xsl:for-each select=
        "ancestor-or-self::*
           [not(descendant::*
                   [self::ScreenSize or self::ConfigurationHotSpots])
           ]">
           <xsl:value-of select="name()"/>
      </xsl:for-each>
  </xsl:variable>
  <xsl:element name="{$vName}"><xsl:value-of select="."/></xsl:element>
 </xsl:template>
 <xsl:template match="text()"/>
</xsl:stylesheet>

当此转换应用于提供的 XML 文档时:

<t>
  <ScreenSize>
    <Width>1440</Width>
    <Height>900</Height>
  </ScreenSize>
  <ConfigurationHotSpots>
    <Rectangle>
      <Location>
        <X>0</X>
        <Y>0</Y>
      </Location>
      <Size>
        <Width>50</Width>
        <Height>50</Height>
      </Size>
      <X>0</X>
      <Y>0</Y>
      <Width>50</Width>
      <Height>50</Height>
    </Rectangle>
  </ConfigurationHotSpots>
</t>

产生了想要的正确结果

<ScreenSizeWidth>1440</ScreenSizeWidth>
<ScreenSizeHeight>900</ScreenSizeHeight>
<ConfigurationHotSpotsRectangleLocationX>0</ConfigurationHotSpotsRectangleLocationX>
<ConfigurationHotSpotsRectangleLocationY>0</ConfigurationHotSpotsRectangleLocationY>
<ConfigurationHotSpotsRectangleSizeWidth>50</ConfigurationHotSpotsRectangleSizeWidth>
<ConfigurationHotSpotsRectangleSizeHeight>50</ConfigurationHotSpotsRectangleSizeHeight>
<ConfigurationHotSpotsRectangleX>0</ConfigurationHotSpotsRectangleX>
<ConfigurationHotSpotsRectangleY>0</ConfigurationHotSpotsRectangleY>
<ConfigurationHotSpotsRectangleWidth>50</ConfigurationHotSpotsRectangleWidth>
<ConfigurationHotSpotsRectangleHeight>50</ConfigurationHotSpotsRectangleHeight>
于 2012-09-13T03:03:53.817 回答
2

这是对 Dimitre 解决方案的调整。根据 OP 的评论,它更简单并利用已知的根元素和 ScreenSize 等的已知位置......

<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="/*">
   <xsl:copy>
     <xsl:apply-templates select="@*|node()" />
   </xsl:copy>
 </xsl:template>

 <xsl:template match="*[not(*)]
                       [ancestor::ScreenSize|ancestor::ConfigurationHotSpots]">   
    <xsl:variable name="vName">
        <xsl:for-each select="ancestor-or-self::*[not(self::t)]">
           <xsl:value-of select="local-name()"/>
        </xsl:for-each>
    </xsl:variable>
  <xsl:element name="{$vName}"><xsl:value-of select="."/></xsl:element>
 </xsl:template>
 <xsl:template match="text()"/>
</xsl:stylesheet>

这是匹配条件的另一种形式...

 <xsl:template match="(ScreenSize//* | ConfigurationHotSpots//*)[not(*)]">  

我实际上不确定哪个更好。如果性能不是什么大问题,请选择更容易阅读的那个。请注意,这意味着每次模板匹配尝试都会扫描两次整个文档。

于 2012-09-13T13:33:58.787 回答