我有一个以下结构的文档(这只是一个帮助我用语言表达问题的示例),我试图将其扁平化。扁平化是指用几个<Event>
复制所有的<Report_Entry>
节点,这样每个<Report_Entry>
节点只包含一个<Event>
What I have:
<?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>
,因为这样我就无法复制它的属性(如果有的话)。是否有其他方法/模板来处理这个问题?
这么简单:
<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>
你的答案再简单不过了,所以在这方面不必担心。在编写代码(尤其是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
s用于隐藏存在于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>
希望这是有启发的。享受吧!