我对之前发布的场景得到了很好的答案,但解决方案无效(或者至少我无法让它用于完整的XML文件)。
我需要一些可以使我当前的XSLT运行得非常好的东西,并添加一些内容来获取这个非常缩进的信息。
<Root>
<Subjects>
<...more XML Data>
<Data>
<...other XML Data>
<Demographic_Information>
<Age1>33</Age1>
<Age2>66</Age2>
<Age3 />
<Age4 />
<Age5 />
<Age6 />
<Age7 />
<Age8 />
<Age9 />
<Age10 />
<Gender1>M</Gender1>
<Gender2>F</Gender2>
<Gender3 />
<Gender4 />
<Gender5 />
<Gender6 />
<Gender7 />
<Gender8 />
<Gender9 />
<Gender10 />
<Race1>W</Race1>
<Race2>H</Race2>
<Race3 />
<Race4 />
<Race5 />
<Race6 />
<Race7 />
<Race8 />
<Race9 />
<Race10 />
</Demographic_Information>
</...other XML Data>
</Data>
</...more XML Data>
</Subjects>
</Root>
我需要人口统计信息的输出就像
一样<Person subject="1">
<Age>33</Age>
<Gender>M</Gender>
<Race>W</Race>
</Person>
<Person subject="2">
<Age>66</Age>
<Gender>F</Gender>
<Race>A</Race>
</Person>
此外,如果对代码中发生的事情做了一点解释,它也更容易学习。我试图逐步完成提供的最后一个版本,但是我在某些方面迷路了,实际上无法分辨它在做什么。
我一如既往地感谢你们的帮助,我对那些给出最佳答案的海报表示感谢。
答案 0 :(得分:0)
如果您要分组的元素的名称是预先知道的,您可以使用其中一个编号元素作为for-each循环或模板的“锚点”,并轻松提取数字。然后,您可以检索所有其他元素:
<xsl:variable name="demo-info-elements">
<Age/><Gender/><Race/>
</xsl:variable>
<xsl:template match="Demographic_Information">
<xsl:variable name="parent" select="."/>
<xsl:for-each select="*[substring(local-name(), 1, 3)=local-name($demo-info-elements/*[1])]">
<xsl:variable name="number" select="substring(local-name(), 4)"/>
<Person name="{$number}">
<xsl:for-each select="$demo-info-elements/*">
<xsl:call-template name="ungrouped-demographic-element">
<xsl:with-param name="name" select="."/>
<xsl:with-param name="number" select="$number"/>
<xsl:with-param name="parent" select="$parent"/>
</xsl:call-template>
</xsl:for-each>
</Person>
</xsl:for-each>
</xsl:template>
<xsl:template name="ungrouped-demographic-element">
<xsl:param name="name"/><xsl:param name="number"/><xsl:param name="parent"/>
<xsl:copy><xsl:value-of select="$parent/*[local-name()=concat(local-name($name), $number)]"/></xsl:copy>
</xsl:template>
如果事先知道元素 ,则转换比较棘手。您需要使用xsl:key
按其尾随数字对元素进行索引。
答案 1 :(得分:0)
跟进我之前的回答(XSLT For-Each loop to get data from enumerated tags?),这需要一个简单的修改。也就是说,既然你说你在某些方面迷路了,我会在最后添加一个解释。
当这个XSLT:
<?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="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable
name="vNums"
select="'0123456789'"/>
<xsl:key
name="kElemByNumber"
match="Demographic_Information/*"
use="translate(name(), translate(name(), $vNums, ''), '')"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="Demographic_Information">
<Demographic_Information>
<xsl:apply-templates
select="*[generate-id() =
generate-id(key(
'kElemByNumber',
translate(name(), translate(name(), $vNums, ''),
''
))[1])][normalize-space()]">
<xsl:sort
select="translate(name(), translate(name(), $vNums, ''), '')"
data-type="number"/>
</xsl:apply-templates>
</Demographic_Information>
</xsl:template>
<xsl:template match="Demographic_Information/*">
<Person subject="{position()}">
<xsl:apply-templates
select="key('kElemByNumber', position())"
mode="children">
<xsl:sort select="name()"/>
</xsl:apply-templates>
</Person>
</xsl:template>
<xsl:template match="Demographic_Information/*" mode="children">
<xsl:element name="{translate(name(), $vNums, '')}">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
...对源XML应用(略有修改,以便“其他/更多XML数据”的实例是注释[从而确保正确的XML文档]):
<Root>
<Subjects>
<!--<...more XML Data>-->
<Data>
<!--<...other XML Data>-->
<Demographic_Information>
<Age1>33</Age1>
<Age2>66</Age2>
<Age3/>
<Age4/>
<Age5/>
<Age6/>
<Age7/>
<Age8/>
<Age9/>
<Age10/>
<Gender1>M</Gender1>
<Gender2>F</Gender2>
<Gender3/>
<Gender4/>
<Gender5/>
<Gender6/>
<Gender7/>
<Gender8/>
<Gender9/>
<Gender10/>
<Race1>W</Race1>
<Race2>H</Race2>
<Race3/>
<Race4/>
<Race5/>
<Race6/>
<Race7/>
<Race8/>
<Race9/>
<Race10/>
</Demographic_Information>
<!--</...other XML Data>-->
</Data>
<!--</...more XML Data>-->
</Subjects>
</Root>
...生成了想要的结果:
<Root>
<Subjects>
<!--<...more XML Data>-->
<Data>
<!--<...other XML Data>-->
<Demographic_Information>
<Person subject="1">
<Age>33</Age>
<Gender>M</Gender>
<Race>W</Race>
</Person>
<Person subject="2">
<Age>66</Age>
<Gender>F</Gender>
<Race>H</Race>
</Person>
</Demographic_Information>
<!--</...other XML Data>-->
</Data>
<!--</...more XML Data>-->
</Subjects>
</Root>
<强>解释强>
让我们一步一步地介绍每一个<xsl:template>
声明。
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</xsl:template>
这个模板(顺便提一下,这个答案和我之前给你的答案之间的唯一变化)有一个工作:复制所有节点(元素,文本节点,注释和处理指令)和所有属性。源XML到结果XML。因此,单独留下这个模板就可以创建原始XML的精确副本。用其他模板覆盖此模板是XSLT中最基本的设计模式之一。
在我们的示例中,此模板确保我们不直接匹配的任何节点/属性(例如所有节点/属性不 <Demographic_Information>
的一部分或其子项)将被复制为 - 是结果文件。
模板#2:修改<Demographic_Information>
<xsl:template match="Demographic_Information">
<Demographic_Information>
<xsl:apply-templates
select="*[generate-id() =
generate-id(key(
'kElemByNumber',
translate(name(), translate(name(), $vNums, ''),
''
))[1])][normalize-space()]">
<xsl:sort
select="translate(name(), translate(name(), $vNums, ''), '')"
data-type="number"/>
</xsl:apply-templates>
</Demographic_Information>
</xsl:template>
此模板(匹配所有<Demographic_Information>
元素)使用称为Muenchian Grouping的技术,这是一种在XSLT 1.0中将类似元素分组在一起的方法。我不打算详细介绍这种技术;快速搜索SO会显示several questions that show the procedure。
一旦掌握了Muenchian Grouping,请记下我在XSLT顶部使用的密钥:
<xsl:key
name="kElemByNumber"
match="Demographic_Information/*"
use="translate(name(), translate(name(), $vNums, ''), '')"/>
实际上,这说:“根据名字的数字部分给我<Demographic_Information>
的所有子元素”。因此,所有“1”元素将组合在一起,所有“2”元素将组合在一起,依此类推。
“名称”的“数字部分”部分是使用Double Translate Method完成的,这是一种仅保留某组字符的方法。在我们的例子中,我们使用该方法仅为那些数字字符选择键。
模板#3:修改<Demographic_Information>
<xsl:template match="Demographic_Information/*">
<Person subject="{position()}">
<xsl:apply-templates
select="key('kElemByNumber', position())"
mode="children">
<xsl:sort select="name()"/>
</xsl:apply-templates>
</Person>
</xsl:template>
请注意,此模板是从以前的模板执行的;因此,它是Muenchian分组序列的一部分。从战术上讲,这意味着该模板仅针对那些数字部分是唯一的节点运行(即,它将针对第一个“1”元素,第一个“2”元素等运行。)
找到<Demographic_Information>
的任何子元素后,系统会指示处理器创建一个新的<Person>
元素,并为其提供一个subject
属性,其中包含工作树中当前位置的值(在这种情况下,是<Demographic_Information>
的所有孩子)。然后,我们将模板应用于我们的密钥中的所有元素(记住,它们由<Demographic_Information>
的子元素组成),这些元素匹配正确的标准,在处理它们时按名称对它们进行排序。
请注意我们为此mode="children"
电话分配的<xsl:apply-templates>
属性。这在我们当前的设置中是必要的:如果我们没有它,那么我们将创建一个无限循环(一个匹配<Demographic_Children>
子项的模板被告知要对<Demographic_Information>
的所有子项应用模板,匹配当前模板等等。)。
模板#4:修改<Demographic_Information>
的子元素(在“子”模式下)
<xsl:template match="Demographic_Information/*" mode="children">
<xsl:element name="{translate(name(), $vNums, '')}">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
这个是相当简单的:对于找到<Demographic_Information>
的每个孩子(在children
模式下),创建一个新元素并给出与当前元素相同的名称,但是使用数字部分除去。最后,通过将模板应用于当前节点的任何子节点来填充此元素的内容(在这种情况下,仅剩下text()
个节点;因此,<xsl:apply-templates>
使用处理器的默认模板,输出文字)。这产生了我们的最终输出。
即使有这样的解释,这里也有很多事情发生。如果我能提供更多清晰度,请告诉我。