XSLT空间分离并添加元素

时间:2012-07-07 21:36:21

标签: xml xslt xslt-1.0 xslt-2.0

我在xml和xslt

中有一个查询

以下是输入XML

<?xml version="1.0" encoding="UTF-8"?>    
<Employer>
    <Employees>
        <EmployeesDetails>van ind 26%</EmployeesDetails>
    </Employees>    
    <Employees>
        <EmployeesDetails>van ind</EmployeesDetails>
    </Employees>    
</Employer>

以上是我的输入文件

以下是我的输出文件

<?xml version="1.0" encoding="UTF-8"?>
<Employer>
    <Employees>
        <Names>van</Names>
        <Location>ind</Location>                
        <Weather>26</Weather>
    </Employees>
    <Employees>
        <Names>van</Names>
        <Location>ind</Location>
        <Weather>100</Weather>
    </Employees>
</Employer>

如何将以下XSLT应用于上述XML输入?

2 个答案:

答案 0 :(得分:1)

<强>予。这个XSLT 2.0转换:

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema"
 exclude-result-prefixes="xs">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

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

 <xsl:template match="Employees">
  <xsl:variable name="vNames" select="tokenize(Names, ' ')"/>
  <xsl:variable name="vLoc" select="tokenize(Location, ' ')"/>
  <xsl:variable name="vWeather"
       select="tokenize(translate(Weather, '%', ' '), ' ')"/>
  <xsl:for-each select="$vNames">
    <xsl:variable name="vPos" select="position()" as="xs:integer"/>
    <Employees>
      <Names><xsl:sequence select="."/></Names>
      <Location>
        <xsl:sequence select="(lower-case($vLoc[$vPos]), 'Unknown')[1]"/>
      </Location>
      <Weather>
        <xsl:sequence select="($vWeather[$vPos], 100)[1]"/>
      </Weather>
    </Employees>
    </xsl:for-each>
 </xsl:template>
</xsl:stylesheet>

应用于提供的XML文档时:

<Employer>
    <Employees>
        <Names>vel bel sel tel mel</Names>
        <Location>IND AUS ENG CAL JAP</Location>
        <Weather>26%</Weather>
    </Employees>
    <Employees>
        <Names>asd sadl asdsel tdddel dmdel</Names>
        <Location>IND AUS ENG CAL JAP</Location>
    </Employees>
</Employer>

会产生想要的正确结果:

<Employer>
   <Employees>
      <Names>vel</Names>
      <Location>ind</Location>
      <Weather>26</Weather>
   </Employees>
   <Employees>
      <Names>bel</Names>
      <Location>aus</Location>
      <Weather>100</Weather>
   </Employees>
   <Employees>
      <Names>sel</Names>
      <Location>eng</Location>
      <Weather>100</Weather>
   </Employees>
   <Employees>
      <Names>tel</Names>
      <Location>cal</Location>
      <Weather>100</Weather>
   </Employees>
   <Employees>
      <Names>mel</Names>
      <Location>jap</Location>
      <Weather>100</Weather>
   </Employees>
      <Employees>
      <Names>asd</Names>
      <Location>ind</Location>
      <Weather>100</Weather>
   </Employees>
   <Employees>
      <Names>sadl</Names>
      <Location>aus</Location>
      <Weather>100</Weather>
   </Employees>
   <Employees>
      <Names>asdsel</Names>
      <Location>eng</Location>
      <Weather>100</Weather>
   </Employees>
   <Employees>
      <Names>tdddel</Names>
      <Location>cal</Location>
      <Weather>100</Weather>
   </Employees>
   <Employees>
      <Names>dmdel</Names>
      <Location>jap</Location>
      <Weather>100</Weather>
   </Employees>
</Employer>

请注意

我做了以下合理的假设:

  1. 您实际上需要100,而不是100%

  2. 您希望处理所有Employees - 不仅是此元素的第一次出现。

  3. 如果提供的位置数量少于提供的名称数量,我还为任何缺失的位置添加了默认值。


    <强> II。 XSLT 1.0解决方案:

    <xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:ext="http://exslt.org/common"
     xmlns:my="my:my" exclude-result-prefixes="ext my">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
     <xsl:strip-space elements="*"/>
     <my:defaults>
      <L>Unknown</L>
      <W>100</W>
     </my:defaults>
    
     <xsl:variable name="vUpper" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
     <xsl:variable name="vLower" select="'abcdefghijklmnopqrstuvwxyz'"/>
    
     <xsl:variable name="vDefaults" select="document('')/*/my:defaults"/>
    
     <xsl:template match="/*">
      <Employer>
       <xsl:apply-templates/>
      </Employer>
     </xsl:template>
    
     <xsl:template match="Employees">
      <xsl:variable name="vrtfNames">
       <xsl:apply-templates select="Names"/>
      </xsl:variable>
      <xsl:variable name="vNames" select="ext:node-set($vrtfNames)/*"/>
      <xsl:variable name="vrtfLocs">
       <xsl:apply-templates select="Location"/>
      </xsl:variable>
      <xsl:variable name="vrtfWeather">
       <xsl:apply-templates select="Weather"/>
      </xsl:variable>
    
      <xsl:apply-templates select="$vNames">
       <xsl:with-param name="pLocs" select="ext:node-set($vrtfLocs)/*"/>
       <xsl:with-param name="pWeather" select="ext:node-set($vrtfWeather)/*"/>
      </xsl:apply-templates>
     </xsl:template>
    
     <xsl:template match="s" priority="3">
      <xsl:param name="pLocs"/>
      <xsl:param name="pWeather"/>
    
      <xsl:variable name="vPos" select="position()"/>
      <Employees>
       <Names><xsl:value-of select="."/></Names>
       <Location>
         <xsl:value-of select=
           "translate($pLocs[position() = $vPos]
                       | $vDefaults[not($pLocs[position() = $vPos])]/L,
                      $vUpper, $vLower)"/>
       </Location>
       <Weather>
         <xsl:value-of select=
           "$pWeather[position() = $vPos]
          | $vDefaults[not($pWeather[position() = $vPos])]/W"/>
       </Weather>
      </Employees>
     </xsl:template>
    
     <xsl:template match="Weather">
      <xsl:call-template name="tokenize">
        <xsl:with-param name="pText" select="translate(., '%', ' ')"/>
      </xsl:call-template>
     </xsl:template>
    
     <xsl:template match="Employees/*/text()" name="tokenize">
      <xsl:param name="pText" select="."/>
    
      <xsl:variable name="vText" select="normalize-space($pText)"/>
      <xsl:if test="$vText">
       <s>
        <xsl:value-of select="substring-before(concat($vText, ' '), ' ')"/>
       </s>
    
       <xsl:call-template name="tokenize">
        <xsl:with-param name="pText" select="substring-after($vText, ' ')"/>
       </xsl:call-template>
      </xsl:if>
     </xsl:template>
    </xsl:stylesheet>
    

    当在提供的XML文档(上面)上应用此转换时,再次生成相同的想要的正确结果

    <Employer>
       <Employees>
          <Names>vel</Names>
          <Location>ind</Location>
          <Weather>26</Weather>
       </Employees>
       <Employees>
          <Names>bel</Names>
          <Location>aus</Location>
          <Weather>100</Weather>
       </Employees>
       <Employees>
          <Names>sel</Names>
          <Location>eng</Location>
          <Weather>100</Weather>
       </Employees>
       <Employees>
          <Names>tel</Names>
          <Location>cal</Location>
          <Weather>100</Weather>
       </Employees>
       <Employees>
          <Names>mel</Names>
          <Location>jap</Location>
          <Weather>100</Weather>
       </Employees>
       <Employees>
          <Names>asd</Names>
          <Location>ind</Location>
          <Weather>100</Weather>
       </Employees>
       <Employees>
          <Names>sadl</Names>
          <Location>aus</Location>
          <Weather>100</Weather>
       </Employees>
       <Employees>
          <Names>asdsel</Names>
          <Location>eng</Location>
          <Weather>100</Weather>
       </Employees>
       <Employees>
          <Names>tdddel</Names>
          <Location>cal</Location>
          <Weather>100</Weather>
       </Employees>
       <Employees>
          <Names>dmdel</Names>
          <Location>jap</Location>
          <Weather>100</Weather>
       </Employees>
    </Employer>
    

    请注意

    1. 基本上实现了与XSLT 2.0转换相同的逻辑。

    2. 由于XPath 1.0没有tokenizelower-case()函数,并且XPath 1.0数据模型中没有 sequence 的概念,因此这些是使用模板进行标记化(分别)使用translate()函数转换为小写,并使用包含天气和位置默认值的元素。

答案 1 :(得分:0)

您的问题在某些关键领域含糊不清。例如,您似乎声明如果<Employees>节点集具有<weather>节点,则应该获得值为100%的节点;那就是说,你的预期输出似乎不一致地应用了那个逻辑。您想要的<Location>结果节点从大写转换为小写。此外,您的输出似乎完全忽略源XML中的第二个<Employees>节点集。

做出一些假设,这是一个使用EXSLT的XSLT 1.0解决方案。如果这不是你想要的,请更新你的问题以便更具体,我会尽力适应。

当这个XSLT:

<?xml version="1.0"?>
<xsl:stylesheet
  xmlns:exsl="http://exslt.org/common"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  exclude-result-prefixes="exsl" 
  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="Employees">
    <xsl:variable name="vNames">
      <xsl:call-template name="tokenize">
        <xsl:with-param name="text" select="Names/text()"/>
      </xsl:call-template>
    </xsl:variable>
    <xsl:variable name="vLocations">
      <xsl:call-template name="tokenize">
        <xsl:with-param name="text" select="Location/text()"/>
      </xsl:call-template>
    </xsl:variable>
    <xsl:apply-templates select="exsl:node-set($vNames)/token">
      <xsl:with-param name="pLocation"
          select="exsl:node-set($vLocations)/token"/>
      <xsl:with-param name="pWeather" select="Weather"/>
    </xsl:apply-templates>
  </xsl:template>

  <xsl:template match="token">
    <xsl:param name="pLocation"/>
    <xsl:param name="pWeather"/>
    <xsl:variable name="vPosition" select="position()"/>
    <Employees>
      <Names>
        <xsl:value-of select="."/>
      </Names>
      <Location>
        <xsl:value-of select="translate($pLocation[$vPosition],
            'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')"/>
      </Location>
      <xsl:choose>
        <xsl:when test="$pWeather != ''">
          <xsl:apply-templates select="$pWeather"/>
        </xsl:when>
        <xsl:otherwise>
          <Weather>100%</Weather>
        </xsl:otherwise>
      </xsl:choose>
    </Employees>
  </xsl:template>

  <xsl:template name="tokenize">
    <xsl:param name="text"/>
    <xsl:param name="delimiter" select="' '"/>
    <xsl:choose>
      <xsl:when test="contains($text,$delimiter)">
        <xsl:element name="token">
          <xsl:value-of select="substring-before($text,$delimiter)"/>
        </xsl:element>
        <xsl:call-template name="tokenize">
          <xsl:with-param name="text"
              select="substring-after($text,$delimiter)"/>
          <xsl:with-param name="delimiter" select="$delimiter"/>
        </xsl:call-template>
      </xsl:when>
      <xsl:when test="$text">
        <xsl:element name="token">
          <xsl:value-of select="$text"/>
        </xsl:element>
      </xsl:when>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

...适用于此XML:

<?xml version="1.0"?>
<Employer>
  <Employees>
    <Names>vel bel sel tel mel</Names>
    <Location>IND AUS ENG CAL JAP</Location>
    <Weather>26%</Weather>
  </Employees>
  <Employees>
    <Names>asd sadl asdsel tdddel dmdel</Names>
    <Location>IND AUS ENG CAL JAP</Location>
  </Employees>
</Employer>

...产生了所需的(?)结果:

<?xml version="1.0" encoding="UTF-8"?>
<Employer>
  <Employees>
    <Names>vel</Names>
    <Location>ind</Location>
    <Weather>26%</Weather>
  </Employees>
  <Employees>
    <Names>bel</Names>
    <Location>aus</Location>
    <Weather>26%</Weather>
  </Employees>
  <Employees>
    <Names>sel</Names>
    <Location>eng</Location>
    <Weather>26%</Weather>
  </Employees>
  <Employees>
    <Names>tel</Names>
    <Location>cal</Location>
    <Weather>26%</Weather>
  </Employees>
  <Employees>
    <Names>mel</Names>
    <Location>jap</Location>
    <Weather>26%</Weather>
  </Employees>
  <Employees>
    <Names>asd</Names>
    <Location>ind</Location>
    <Weather>100%</Weather>
  </Employees>
  <Employees>
    <Names>sadl</Names>
    <Location>aus</Location>
    <Weather>100%</Weather>
  </Employees>
  <Employees>
    <Names>asdsel</Names>
    <Location>eng</Location>
    <Weather>100%</Weather>
  </Employees>
  <Employees>
    <Names>tdddel</Names>
    <Location>cal</Location>
    <Weather>100%</Weather>
  </Employees>
  <Employees>
    <Names>dmdel</Names>
    <Location>jap</Location>
    <Weather>100%</Weather>
  </Employees>
</Employer>

<强>解释

  1. 第一个模板 - Identity Template - 按原样复制所有元素和属性。
  2. 第二个与<Employee>元素匹配的模板运行一个特殊的tokenize模板,其作用是将空格分隔的字符串拆分为结果树片段。为方便起见,这些片段保存在变量中。
  3. 第三个模板匹配名为token的所有元素。这些是由EXSLT生成的,它将结果树片段转换为由<token>元素组成的节点集。对于其中的每一个,我们提取必要的元素值以创建<Names><Location>。此模板还包含确定<weather>元素所需值的必要逻辑。