合并连续后代节点到一个(Merge successive descendant nodes into

2019-09-22 08:59发布

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>

提前致谢!

Answer 1:

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

稍微不同的方法可以采取,不那么遥远从目前你在做什么,是不是直接跳转到“叶”元素,匹配反过来每一个级别,但保留元素名称,其传递的运行级联从由参数的一个电平到anoter。

试试这个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>


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


Answer 3:

这里是Dimitre的解决方案的调整。 这是简单,利用断知根元素和屏幕尺寸等的已知位置,按OP的评论......

<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(*)]">  

我不肯定实际上这是更好的。 如果性能是不是什么大不了的,去与以往任何时候都更容易读取。 只是要小心,这意味着每个模板匹配尝试两个完整的文档扫描。



文章来源: Merge successive descendant nodes into one