根据元素属性类型对XML进行分类

时间:2013-05-14 22:16:16

标签: xslt

使用此XML输入,我无法将元素添加到特定部分。

<Country>
  <info enum="CTRY" name="United Sates of America" total-states="50" />
  <info enum="ST" name="New York" population="8,244,910"/>
  <info enum="ST" name="Chicago" population="2,707,120"/>
  <info enum="CTRY" name="Germany" total-states="16"/>
  <info enum="ST" name="Berlin" population="3,469,910"/>
  <info enum="ST" name="Brandenburg" population="2,500,000"/>
</Country>

这是我的XSL,

<xsl:template match="/">
  <Country>
    <xsl:for-each select="Country/info">
      <xsl:if test="@enum='CTRY'">
        <CountryInfo>
          <name>Country Name: <xsl:value-of select="@name"/></name>
          <districts><xsl:value-of select="@total-states"></xsl:value-of></districts>
          <xsl:for-each select="/Country/info">
            <xsl:if test="@enum='ST'">
              <state>
                <stateName>State Name: <xsl:value-of select="@name"/></stateName>
                <statePop>State Population: <xsl:value-of select="@population"/></statePop>
              </state>
            </xsl:if>
          </xsl:for-each>
        </CountryInfo>
      </xsl:if>
    </xsl:for-each>
  </Country>
</xsl:template>

问题在于所有州都出现在这两个国家。

这是我想要生成的XML输出,

<Country>
  <CountryInfo>
    <name>Country Name: United Sates of America</name>
    <districts>50</districts>
    <state>
      <stateName>State Name: New York</stateName>
      <statePop>State Population: 8,244,910</statePop>
    </state>
    <state>
      <stateName>State Name: Chicago</stateName>
      <statePop>State Population: 2,707,120</statePop>
    </state>
  </CountryInfo>
  <CountryInfo>
    <name>Country Name: Germany</name>
    <districts>16</districts>
    <state>
      <stateName>State Name: Berlin</stateName>
      <statePop>State Population: 3,469,910</statePop>
    </state>
    <state>
      <stateName>State Name: Brandenburg</stateName>
      <statePop>State Population: 2,500,000</statePop>
    </state>
  </CountryInfo>
</Country>

是否可以使用XSLT实现此目的?

3 个答案:

答案 0 :(得分:1)

您的来源是对XML的可怕滥用!你应该向那些设计并向你提供这种垃圾的人抱怨。

使用单个模板,除了删除或扩展源中已有的元素之外,您无法执行任何操作。

我相信这种转变可以满足您的需求。它的工作原理是复制Country根元素并处理其内容。第二个模板匹配info属性为enum的所有CTRY元素,这些元素构成了CountryInfo输出元素的基础。

状态信息必须以递归方式完成,方法是使用call-template插入来自以下info元素的信息(如果enum属性为ST。< / p>

由于源数据的结构,这种转换非常脆弱,如果有任何意外的元素,它将会中断。请注意。

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

  <xsl:strip-space elements="*"/>
  <xsl:output method="xml" indent="yes"/>

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

  <xsl:template match="info[@enum='CTRY']">
    <CountryInfo>
      <name>
        <xsl:text>Country name: </xsl:text>
        <xsl:value-of select="@name"/>
      </name>
      <districts>
        <xsl:value-of select="@total-states"/>
      </districts>
      <xsl:call-template name="state"/>
    </CountryInfo>
  </xsl:template>

  <xsl:template name="state">
    <xsl:param name="i" select="1"/>
    <xsl:if test="following-sibling::info[$i][@enum='ST']">
      <state>
        <stateName>
          <xsl:text>State Name: </xsl:text>
          <xsl:value-of select="following-sibling::info[$i]/@name"/>
        </stateName>
        <statePop>
          <xsl:text>State Population: </xsl:text> 
          <xsl:value-of select="following-sibling::info[$i]/@population"/>
        </statePop>
      </state>
      <xsl:call-template name="state">
        <xsl:with-param name="i" select="$i+1"/>
      </xsl:call-template>
    </xsl:if>
  </xsl:template>

</xsl:stylesheet>

<强>输出

<?xml version="1.0" encoding="utf-8"?>
<Country>
   <CountryInfo>
      <name>Country name: United States of America</name>
      <districts>50</districts>
      <state>
         <stateName>State Name: New York</stateName>
         <statePop>State Population: 8,244,910</statePop>
      </state>
      <state>
         <stateName>State Name: Chicago</stateName>
         <statePop>State Population: 2,707,120</statePop>
      </state>
   </CountryInfo>
   <CountryInfo>
      <name>Country name: Germany</name>
      <districts>16</districts>
      <state>
         <stateName>State Name: Berlin</stateName>
         <statePop>State Population: 3,469,910</statePop>
      </state>
      <state>
         <stateName>State Name: Brandenburg</stateName>
         <statePop>State Population: 2,500,000</statePop>
      </state>
   </CountryInfo>
</Country>

答案 1 :(得分:0)

您正在制作所有州的完整列表,因为在您的<xsl:for-each>内,您有另一个<xsl:for-each>选择所有州的“跳起来”到具有/Country/info的根节点。

在这种情况下,对于每个“国家/地区”元素,您希望找到info信息@enum='ST' who's nearest preceding @ enum ='CTRY'的所有element with元素。产生“状态”。

不是从“拉”式接近它,而是“推送”正确的内容并匹配适用的模式。它将使您的XSLT更容易生成并帮助模块化为更易于维护的不同模板(如果/当您进行导入时更容易覆盖)。

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

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

    <xsl:template match="Country">
     <xsl:copy>
         <xsl:apply-templates select="info[@enum='CTRY']"/>
     </xsl:copy>
    </xsl:template>

    <xsl:template match="info[@enum='CTRY']">
        <CountryInfo>
            <name><xsl:value-of select="@name"/></name>
            <districts><xsl:value-of select="@total-states"/></districts>
            <xsl:apply-templates 
                       select="following-sibling::info[@enum='ST']
                                  [generate-id(
                                      preceding-sibling::info[@enum='CTRY'][1])
                                   = generate-id(current())]"/>
        </CountryInfo>
    </xsl:template>

    <xsl:template match="info[@enum='ST']">
        <state>
            <stateName>
                <xsl:text>State Name: </xsl:text>
                <xsl:value-of select="@name"/>
            </stateName>
            <statePop>
                <xsl:text>State Population: </xsl:text>
                <xsl:value-of select="@population"/>
            </statePop>
        </state>
    </xsl:template>

</xsl:stylesheet>

答案 2 :(得分:0)

这个简短而简单的转型

<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:key name="kStates" match="info[@enum='ST']"
          use="generate-id(preceding-sibling::info[@enum='CTRY'][1])"/>

 <xsl:template match="/*">
  <Country>
    <xsl:apply-templates select="info[@enum='CTRY']"/>
  </Country>
 </xsl:template>

 <xsl:template match="info[@enum='CTRY']">
  <CountryInfo>
    <name>Country Name: <xsl:value-of select="@name"/></name>
    <districts><xsl:value-of select="@total-states"/></districts>
    <xsl:apply-templates select="key('kStates', generate-id())"/>
  </CountryInfo>
 </xsl:template>

 <xsl:template match="info[@enum='ST']">
  <state>
    <stateName>State Name:<xsl:value-of select="@name"/></stateName>
    <statePop>State Population: <xsl:value-of select="@population"/></statePop>
  </state>
 </xsl:template>
</xsl:stylesheet>

应用于提供的XML文档时(完全不可怕):):

<Country>
  <info enum="CTRY" name="United Sates of America" total-states="50" />
  <info enum="ST" name="New York" population="8,244,910"/>
  <info enum="ST" name="Chicago" population="2,707,120"/>
  <info enum="CTRY" name="Germany" total-states="16"/>
  <info enum="ST" name="Berlin" population="3,469,910"/>
  <info enum="ST" name="Brandenburg" population="2,500,000"/>
</Country>

会产生想要的正确结果:

<Country>
   <CountryInfo>
      <name>Country Name: United Sates of America</name>
      <districts>50</districts>
      <state>
         <stateName>State Name:New York</stateName>
         <statePop>State Population: 8,244,910</statePop>
      </state>
      <state>
         <stateName>State Name:Chicago</stateName>
         <statePop>State Population: 2,707,120</statePop>
      </state>
   </CountryInfo>
   <CountryInfo>
      <name>Country Name: Germany</name>
      <districts>16</districts>
      <state>
         <stateName>State Name:Berlin</stateName>
         <statePop>State Population: 3,469,910</statePop>
      </state>
      <state>
         <stateName>State Name:Brandenburg</stateName>
         <statePop>State Population: 2,500,000</statePop>
      </state>
   </CountryInfo>
</Country>