使用XSLT将XML转换为固定长度的文本文件

时间:2016-05-19 13:19:14

标签: c# .net xml xslt

我需要将XML转换为固定宽度样式的文本文件,并且必须使用XSLT方法来执行此操作。

我是第一次使用XSLT用户而且我已经了解了基础知识,但是这个源XML的某些方面使得它变得困难......至少对我而言。

<Prescription>
<Drugs>
    <Drug id="1">
        <DrugName>Red Tablets</DrugName>
    </Drug>
    <Drug id="2">
        <DrugName>Blue Tablets</DrugName>
    </Drug>
</Drugs>
<Patients>
    <Patient id="20">
        <Surname>Doe</Surname>
        <Forenames>John</Forenames>
        <Items>
            <Item>
                <ProductID>1</ProductID>
                <AdminEvent date="2016-05-11" hour="7" qty="1"/>
                <AdminEvent date="2016-05-12" hour="7" qty="1"/>
            </Item>
        </Items>
    </Patient>
    <Patient id="50">
        <Surname>Doe</Surname>
        <Forenames>Jane</Forenames>
        <Items>
            <Item>
                <ProductID>2</ProductID>
                <AdminEvent date="2016-05-11" hour="7" qty="1"/>
                <AdminEvent date="2016-05-12" hour="7" qty="1"/>
            </Item>
        </Items>
    </Patient>
<Patients>

这是一种含有2种药物和2名患者的处方。患者John在早上7点有红色药片2天,Jane服用蓝色药片。我需要把它变成一个纯文本文件,如:

[ForeName]+[Surname]+[DrugName]+[DrugID]+[Hour]+[Qty]+[Date]

所以在这个例子中:

John Doe    Blue Tablets    1   7   1   2016-05-11
John Doe    Blue Tablets    1   7   1   2016-05-12
Jane Doe    Red Tablets     2   7   1   2016-05-11
Jane Doe    Red Tablets     2   7   1   2016-05-12

我正在努力解决以下问题: a)参考不同的分支(EG从Item元素回溯到Drug) b)仅在项目部分中获取ID的DrugName

希望这一切都有意义!

4 个答案:

答案 0 :(得分:1)

XSLT 1.0

此变换将动态调整患者和药物名称的字段长度...如果它们已修复,您可以进一步简化此操作。

<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:msxsl="urn:schemas-microsoft-com:xslt"
                exclude-result-prefixes="msxsl"
>
  <xsl:output method="text" indent="yes"/>

  <xsl:variable name="name-max-length">
    <xsl:for-each select="//Patient">
      <xsl:sort select="string-length(concat(Forenames,' ',Surname))" data-type="number" />
      <xsl:if test="position() = last()">
        <xsl:value-of select="string-length(concat(Forenames,' ',Surname))" />
      </xsl:if>
    </xsl:for-each>
  </xsl:variable>

  <xsl:variable name="drug-max-length">
    <xsl:for-each select="//DrugName">
      <xsl:sort select="string-length(.)" data-type="number" />
      <xsl:if test="position() = last()">
        <xsl:value-of select="string-length(.)" />
      </xsl:if>
    </xsl:for-each>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:apply-templates select="//AdminEvent"/>
  </xsl:template>

  <xsl:template match="AdminEvent">
    <xsl:variable name="name" select="concat(ancestor::Patient/Forenames,' ',ancestor::Patient/Surname)" />

    <xsl:variable name="productId" select="ancestor::Item/ProductID/text()"/>
    <xsl:variable name="drug" select="ancestor::Prescription/Drugs/Drug[@id=$productId]/DrugName"/>

    <xsl:value-of select="substring(concat($name,'                    '),1,($name-max-length + 4))"/>
    <xsl:value-of select="substring(concat($drug,'                    '),1,($drug-max-length + 4))"/>

    <xsl:value-of select="substring(concat($productId,'                    '),1,5)"/>

    <xsl:value-of select="substring(concat(@hour,'                    '),1,5)"/>
    <xsl:value-of select="substring(concat(@qty,'                    '),1,5)"/>
    <xsl:value-of select="substring(concat(@date,'                    '),1,10)"/>
    <xsl:text>&#10;</xsl:text>
  </xsl:template>
</xsl:stylesheet>

<强>结果

John Doe    Red Tablets     1    7    1    2016-05-11
John Doe    Red Tablets     1    7    1    2016-05-12
Jane Doe    Blue Tablets    2    7    1    2016-05-11
Jane Doe    Blue Tablets    2    7    1    2016-05-12

答案 1 :(得分:0)

  

我正在努力解决以下问题:a)提到不同的分支   (EG从Item元素回溯到药物)b)只有得到   项目部分中的ID的DrugName

使用查找ProductID引用的药物很容易解决。例如,以下样式表:

XSLT 1.0

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

<xsl:key name="drug" match="Drug" use="@id" />

<xsl:template match="Prescription">
    <xsl:for-each select="Patients/Patient">
        <xsl:variable name="surname" select="Surname" />
        <xsl:variable name="forename" select="Forenames" />
        <xsl:for-each select="Items/Item">
            <xsl:variable name="drugname" select="key('drug', ProductID)/DrugName" />
            <xsl:for-each select="AdminEvent">
                <xsl:value-of select="$surname" />
                <xsl:text> | </xsl:text>
                <xsl:value-of select="$forename" />
                <xsl:text> | </xsl:text>
                <xsl:value-of select="$drugname" />
                <xsl:text> | </xsl:text>
                <xsl:value-of select="@qty" />
                <xsl:text> | </xsl:text>
                <xsl:value-of select="@date" />
                <xsl:text>&#10;</xsl:text>
            </xsl:for-each>
        </xsl:for-each>
    </xsl:for-each>
</xsl:template>

</xsl:stylesheet>

将返回:

Doe | John | Red Tablets | 1 | 2016-05-11
Doe | John | Red Tablets | 1 | 2016-05-12
Doe | Jane | Blue Tablets | 1 | 2016-05-11
Doe | Jane | Blue Tablets | 1 | 2016-05-12

我建议您另外提出一个关于生成固定宽度文件的问题。

答案 2 :(得分:0)

使用XSLT 2.0,您可以使用

进行相当紧凑的操作
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="2.0">

    <xsl:output method="text"/>

    <xsl:key name="drug-by-id" match="Drugs/Drug" use="@id"/>

    <xsl:template match="/">
        <xsl:apply-templates select="//AdminEvent"/>
    </xsl:template>

    <xsl:template match="AdminEvent">
        <xsl:value-of
            select="ancestor::Patient/concat(Forenames, ' ', Surname), key('drug-by-id', ../ProductID)/DrugName, ../ProductID, @qty, @date"/>
        <xsl:text>&#10;</xsl:text>
    </xsl:template>

</xsl:stylesheet>

答案 3 :(得分:0)

以下是适用于您的示例的XSLT:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>

<xsl:template match="/">
  <xsl:apply-templates select="//AdminEvent"/>
</xsl:template>

<xsl:template match="AdminEvent">
  <xsl:value-of select="ancestor::Patient/Forenames"/>
  <xsl:text> </xsl:text>
  <xsl:value-of select="ancestor::Patient/Surname"/>
  <xsl:text> '</xsl:text>
  <xsl:variable name="pid" select="ancestor::Item/ProductID/text()"/>
  <xsl:value-of select="ancestor::Prescription/Drugs/Drug[@id=$pid]/DrugName"/>
  <xsl:text>'
</xsl:text>
</xsl:template>

</xsl:stylesheet>

请注意,我匹配应该生成输出行(AdminSample)的每个元素,然后根据当前节点收集数据。

通过ancestor我从当前的AdminSample节点向上移动,直到找到正确的父节点(Patient),然后再从那里向下移动,并在{Drug上添加一个包围搜索条件{1}}节点选择与ProductID变量中存储的pid匹配的正确值。阅读有关XPath轴here和XSLT变量here的信息。

现在添加AdminEvent本身的其他所需值对您来说应该是微不足道的: - )