使用XSLT展平已重复子节点的节点

时间:2013-05-13 18:03:05

标签: xslt-1.0

我有一份关于以下结构的文件(这只是一个帮助我用语言表达问题的例子),我正试图压扁。扁平化我的意思是复制所有<Report_Entry>个节点,其中包含多个<Event>个节点,以便每个<Report_Entry>节点只包含一个<Event>

我有什么:

<?xml version="1.0"?>
<Report_Data>
  <Report_Entry>
    <ID>1</ID>
    <Event>
      <Start_Date>2011-09-06</Start_Date>
      <End_Date>2011-09-10</End_Date>
    </Event>
    <Event>
      <Start_Date>2011-09-10</Start_Date>
      <End_Date>2011-09-15</End_Date>
    </Event>
    <Event>
      <Start_Date>2011-09-15</Start_Date>
      <End_Date>2011-09-20</End_Date>
    </Event>
  </Report_Entry>
  <Report_Entry>
    <ID>2</ID>
    <Event>
      <Start_Date>2011-09-20</Start_Date>
      <End_Date>2011-09-25</End_Date>
    </Event>
    <Event>
      <Start_Date>2011-09-25</Start_Date>
      <End_Date>2011-09-30</End_Date>
    </Event>
  </Report_Entry>
  <Report_Entry>
    <ID>3</ID>
    <Event>
      <Start_Date>2011-09-30</Start_Date>
      <End_Date>2011-10-05</End_Date>
    </Event>
  </Report_Entry>
</Report_Data>

我想要的是:

<?xml version="1.0"?>
<Report_Data>
  <Report_Entry>
    <ID>1</ID>
    <Event>
      <Start_Date>2011-09-06</Start_Date>
      <End_Date>2011-09-10</End_Date>
    </Event>
  </Report_Entry>
  <Report_Entry>
    <ID>1</ID>
    <Event>
      <Start_Date>2011-09-10</Start_Date>
      <End_Date>2011-09-15</End_Date>
    </Event>
  </Report_Entry>
  <Report_Entry>
    <ID>1</ID>
    <Event>
      <Start_Date>2011-09-15</Start_Date>
      <End_Date>2011-09-20</End_Date>
    </Event>
  </Report_Entry>
  <Report_Entry>
    <ID>2</ID>
    <Event>
      <Start_Date>2011-09-20</Start_Date>
      <End_Date>2011-09-25</End_Date>
    </Event>
  </Report_Entry>
  <Report_Entry>
    <ID>2</ID>
    <Event>
      <Start_Date>2011-09-25</Start_Date>
      <End_Date>2011-09-30</End_Date>
    </Event>
  </Report_Entry>
  <Report_Entry>
    <ID>3</ID>
    <Event>
      <Start_Date>2011-09-30</Start_Date>
      <End_Date>2011-10-05</End_Date>
    </Event>
  </Report_Entry>
</Report_Data>

这是我正在使用的XSLT:

<?xml version="1.0" encoding="UTF-8"?>
<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:template match="Report_Entry">
  <xsl:for-each select="Event">
    <Report_Entry>
      <xsl:copy-of select="../*[not(self::Event)]"/>
      <xsl:copy-of select="."/>
    </Report_Entry>
  </xsl:for-each>
</xsl:template>

</xsl:stylesheet>

它有效,但我觉得可能有更好,更快,更通用的解决方案。特别是,我不喜欢“硬编码”<Report_Entry>,因为这样我就无法复制其属性(如果有的话)。还有其他方法/模板来处理这个问题吗?

2 个答案:

答案 0 :(得分:1)

就这么简单

<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="/*">
  <Report_Data>
    <xsl:apply-templates select="*/Event"/>
  </Report_Data>
 </xsl:template>

 <xsl:template match="Event">
  <Report_Entry>
   <xsl:copy-of select="../ID | ."/>
  </Report_Entry>
 </xsl:template>
</xsl:stylesheet>

在提供的XML文档上应用此转换时:

<Report_Data>
  <Report_Entry>
    <ID>1</ID>
    <Event>
      <Start_Date>2011-09-06</Start_Date>
      <End_Date>2011-09-10</End_Date>
    </Event>
    <Event>
      <Start_Date>2011-09-10</Start_Date>
      <End_Date>2011-09-15</End_Date>
    </Event>
    <Event>
      <Start_Date>2011-09-15</Start_Date>
      <End_Date>2011-09-20</End_Date>
    </Event>
  </Report_Entry>
  <Report_Entry>
    <ID>2</ID>
    <Event>
      <Start_Date>2011-09-20</Start_Date>
      <End_Date>2011-09-25</End_Date>
    </Event>
    <Event>
      <Start_Date>2011-09-25</Start_Date>
      <End_Date>2011-09-30</End_Date>
    </Event>
  </Report_Entry>
  <Report_Entry>
    <ID>3</ID>
    <Event>
      <Start_Date>2011-09-30</Start_Date>
      <End_Date>2011-10-05</End_Date>
    </Event>
  </Report_Entry>
</Report_Data>

产生了想要的正确结果:

<Report_Data>
   <Report_Entry>
      <ID>1</ID>
      <Event>
         <Start_Date>2011-09-06</Start_Date>
         <End_Date>2011-09-10</End_Date>
      </Event>
   </Report_Entry>
   <Report_Entry>
      <ID>1</ID>
      <Event>
         <Start_Date>2011-09-10</Start_Date>
         <End_Date>2011-09-15</End_Date>
      </Event>
   </Report_Entry>
   <Report_Entry>
      <ID>1</ID>
      <Event>
         <Start_Date>2011-09-15</Start_Date>
         <End_Date>2011-09-20</End_Date>
      </Event>
   </Report_Entry>
   <Report_Entry>
      <ID>2</ID>
      <Event>
         <Start_Date>2011-09-20</Start_Date>
         <End_Date>2011-09-25</End_Date>
      </Event>
   </Report_Entry>
   <Report_Entry>
      <ID>2</ID>
      <Event>
         <Start_Date>2011-09-25</Start_Date>
         <End_Date>2011-09-30</End_Date>
      </Event>
   </Report_Entry>
   <Report_Entry>
      <ID>3</ID>
      <Event>
         <Start_Date>2011-09-30</Start_Date>
         <End_Date>2011-10-05</End_Date>
      </Event>
   </Report_Entry>
</Report_Data>

答案 1 :(得分:0)

你的答案简单得多,所以不必担心这方面。在编写代码时,尤其是XSLT时,代码的清晰度往往比它的最终效率值得多。

对于硬编码元素名称和复制属性,这是一个开头:

<xsl:template match="Report_Entry">
  <xsl:variable name="parent-name" select="name()"/>
  <xsl:variable name="parent-attributes" select="@*"/>
  <xsl:for-each select="Event">
    <xsl:element name="{$parent-name}">
      <xsl:copy-of select="$parent-attributes"/>
      <xsl:copy-of select="../*[not(self::Event)]"/>
      <xsl:copy-of select="."/>
    </xsl:element>
  </xsl:for-each>
</xsl:template>

variable用于存储for-each之外的某些上下文。 element制作一个与原作相似的元素,无论它叫什么,第一个copy-of通过复制原始属性使其更具说服力。现在,如果您的数据突然出现属性,那么您就可以了。

在这种情况下,名称的非硬编码并不意味着什么,但是,如果您将该部分分解为单独的模板并从多个位置调用它,那么它就会出现:

<xsl:template name="collapse-the-thing">
  <xsl:param name="context"/>
  <xsl:param name="sub-element-name" select="'Event'"/>
  <xsl:variable name="parent-name" select="name($context)"/>
  <xsl:variable name="parent-attributes" select="$context/@*"/>
  <xsl:for-each select="$context/*[name()=$sub-element-name]">
    <xsl:element name="{$parent-name}">
      <xsl:copy-of select="$parent-attributes"/>
      <xsl:copy-of select="../*[name()!=$sub-element-name]"/>
      <xsl:copy-of select="."/>
    </xsl:element>
  </xsl:for-each>
</xsl:template>

<xsl:template match="Report_Entry">
  <xsl:call-template name="collapse-the-thing">
    <xsl:with-param name="context" select="."/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="Some_Other_Entry">
  <xsl:call-template name="collapse-the-thing">
    <xsl:with-param name="context" select="."/>
    <xsl:param name="sub-element-name" select="'Happening'"/>
  </xsl:call-template>
</xsl:template>

希望这很有启发性。享受!