我希望像下面那样转换一些XML,在此示例中,只有2个元素可以组合在一起,但是可以更多:
<file>
<patient>
<Lab_No_Spec_No>12345</Lab_No_Spec_No>
<Patient_Number>ABC</Patient_Number>
<Albumin_g_L>48 </Albumin_g_L>
<Calcium_mmol_L>
<Phosphate_mmol_L>100 </Phosphate_mmol_L>
</patient>
<patient>
<Lab_No_Spec_No>12345</Lab_No_Spec_No>
<Patient_Number>ABC</Patient_Number>
<Albumin_g_L>92 </Albumin_g_L>
<Calcium_mmol_L>50 </Calcium_mmol_L>
<Phosphate_mmol_L/>
</patient>
</file>
得到以下结果:
<file>
<patient>
<Lab_No_Spec_No>12345</Lab_No_Spec_No>
<Patient_Number>ABC</Patient_Number>
<Albumin_g_L>48,92</Albumin_g_L>
<Calcium_mmol_L>50</Calcium_mmol_L>
<Phosphate_mmol_L>100</Phosphate_mmol_L>
</patient>
</file>
到目前为止,这是我的转型:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="specimen" match="patient" use="Lab_No_Spec_No" />
<xsl:template match="file">
<file>
<xsl:for-each select="patient[count(. | key('specimen', Lab_No_Spec_No)[1]) = 1]">
<patient>
<xsl:copy-of select="Lab_No_Spec_No" />
<xsl:copy-of select="Patient_Number" />
<Albumin_g_L>
<xsl:for-each select="key('specimen', Lab_No_Spec_No)">
<xsl:value-of select="normalize-space(Albumin_g_L)" />
<xsl:if test="position() != last()">
<xsl:text>,</xsl:text>
</xsl:if>
</xsl:for-each>
</Albumin_g_L>
<Calcium_mmol_L>
<xsl:for-each select="key('specimen', Lab_No_Spec_No)">
<xsl:value-of select="normalize-space(Calcium_mmol_L)" />
<xsl:if test="position() != last()">
<xsl:text>,</xsl:text>
</xsl:if>
</xsl:for-each>
</Calcium_mmol_L>
<Phosphate_mmol_L>
<xsl:for-each select="key('specimen', Lab_No_Spec_No)">
<xsl:value-of select="normalize-space(Phosphate_mmol_L)" />
<xsl:if test="position() != last()">
<xsl:text>,</xsl:text>
</xsl:if>
</xsl:for-each>
</Phosphate_mmol_L>
</patient>
</xsl:for-each>
</file>
</xsl:template>
</xsl:stylesheet>
我正在寻求有关上述内容的一些问题:
在<xsl:for-each select="key('specimen', Lab_No_Spec_No)">
串联期间包含空元素,我想省略这些元素,以免得到类似<Calcium_mmol_L>50,</Calcium_mmol_L>
的结果。什么需要更改我的for-each选择,以便不选择空元素?
真正的源XML文件包含30多个元素,我需要对其进行串联。我在示例中对3个元素重复了相同的转换,但是对于Patient_Number
元素之后的元素是否有一种简便的方法?还是必须重复进行转换?类似于:
<xsl:for-each select="following-sibling::Patient_Number"> <local-name(.)> <xsl:for-each select="key('specimen', Lab_No_Spec_No)"> <xsl:value-of select="normalize-space(.)" /> <xsl:if test="position() != last()"> <xsl:text>,</xsl:text> </xsl:if> </xsl:for-each> </local-name(.)> </xsl:for-each>
答案 0 :(得分:1)
要排除空元素,只需在您的xsl:for-each
中添加一个条件
<xsl:for-each select="key('specimen', Lab_No_Spec_No)[normalize-space(Albumin_g_L)]">
为避免重复,您将变得很聪明,并对每个Lab_No_Spec_No的列名称进行第二级分组。
尝试使用此XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="specimen" match="patient" use="Lab_No_Spec_No" />
<xsl:key name="cols" match="patient/*" use="concat(../Lab_No_Spec_No, '|', local-name())" />
<xsl:template match="file">
<file>
<xsl:for-each select="patient[count(. | key('specimen', Lab_No_Spec_No)[1]) = 1]">
<patient>
<xsl:copy-of select="Lab_No_Spec_No" />
<xsl:copy-of select="Patient_Number" />
<xsl:apply-templates select="*[not(self::Lab_No_Spec_No) and not(self::Patient_Number)]
[count(. | key('cols', concat(../Lab_No_Spec_No, '|', local-name()))[1]) = 1]" />
</patient>
</xsl:for-each>
</file>
</xsl:template>
<xsl:template match="patient/*">
<xsl:copy>
<xsl:for-each select="key('cols', concat(../Lab_No_Spec_No, '|', local-name()))[normalize-space()]">
<xsl:value-of select="normalize-space(.)" />
<xsl:if test="position() != last()">
<xsl:text>,</xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
尽管如此,在XSLT 2.0中这一切要容易得多。...
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="file">
<file>
<xsl:for-each-group select="patient" group-by="Lab_No_Spec_No">
<patient>
<xsl:copy-of select="Lab_No_Spec_No,Patient_Number" />
<xsl:for-each-group select="current-group()/(* except (Lab_No_Spec_No, Patient_Number))" group-by="local-name()">
<xsl:copy>
<xsl:value-of select="current-group()[normalize-space()]/normalize-space()" separator="," />
</xsl:copy>
</xsl:for-each-group>
</patient>
</xsl:for-each-group>
</file>
</xsl:template>
</xsl:stylesheet>
答案 1 :(得分:1)
Tim已经提供了一个很好的答案,但是我认为,从身份转换模板开始,然后仅为需要转换的那些元素添加模板,这些问题将受益匪浅。另外,只要输入中的每个patient
都具有相同的child
元素,我认为您可以简单地按组进行分组,然后只处理每个组中第一个patient
元素的子元素,然后在需要连接数据的那些元素的模板中,可以使用建议的键将其推入另一种模式以输出列表:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="xml" indent="yes" version="5"/>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:key name="specimen" match="patient" use="Lab_No_Spec_No" />
<xsl:key name="child" match="patient/*" use="concat(../Lab_No_Spec_No, '|', local-name())"/>
<xsl:template match="patient[count(. | key('specimen', Lab_No_Spec_No)[1]) = 1]">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="patient[not(count(. | key('specimen', Lab_No_Spec_No)[1]) = 1)]"/>
<xsl:template match="patient/*[not(self::Lab_No_Spec_No|self::Patient_Number)]">
<xsl:copy>
<xsl:apply-templates select="key('child', concat(../Lab_No_Spec_No, '|', local-name()))[normalize-space()]" mode="value"/>
</xsl:copy>
</xsl:template>
<xsl:template match="patient/*" mode="value">
<xsl:if test="position() > 1">,</xsl:if>
<xsl:value-of select="normalize-space()"/>
</xsl:template>
</xsl:stylesheet>
这并不能使您摆脱在注释中提出的问题,即必须在某个位置列出所有不想连接的元素,但是只有一个match
属性谓词需要在哪里做那个。