使用XSL将日期从三个元素(MM,DD,YYYY)转换为一个

时间:2016-12-20 23:09:25

标签: xslt

我从XML文件导入SQL Server。日期存储如下:

    <BIRTH_YEAR> 1943 </BIRTH_YEAR>
    <BIRTH_MONTH>  04 </BIRTH_MONTH>
    <BIRTH_DAY>    01 </BIRTH_DAY>

当然,我希望将此转换为简单的日期。在不同级别(RENEW_EXPIRE_等),此结构非常一致地重复。但是,演示顺序(年,月,日)可能会有所不同。这是我的XSL解决方案:

<xsl:template name="date_value">
    <xsl:param name="my_element_prefix"/>
    <xsl:param name="DD"/>
    <xsl:param name="MM"/>
    <xsl:param name="YYYY"/>

    <!-- Produce a field <BIRTH_DATE> -->
    <xsl:element name="{concat($my_element_prefix,'_DATE')}">
        <xsl:value-of select="concat($MM, '/', $DD, '/', $YYYY)"/>
    </xsl:element>

</xsl:template>

我不太喜欢它。我必须准备三个日期组件参数才能调用该函数。我希望从函数中的 中获取这些值。这可以通过使用$my_element_prefix来指定源节点来完成。但事实证明,使用参数构建XPath表达式并不简单。

我可以将这些值合并为加载到SQL Server中的一部分。但我宁愿在这个230 Mb文件上预先加载所有转换,我可能会这样做。

您对如何处理此问题的见解?

4 个答案:

答案 0 :(得分:1)

看起来你的背景是非XSLT编程。 :)

对于可靠的XSLT解决方案,您需要更改方法。

目前还不完全清楚你要做什么。为了论证,我对此示例做了以下假设:

  • <BIRTH_YEAR><BIRTH_MONTH><BIRTH_DAY>包含在父元素中,此处假设为<PERSON>,并且它们仅在该父元素的每个实例中出现一次

所以给出如下任意数据:

<RECORDS>
    <PERSON name="A">
        <BIRTH_YEAR> 1943 </BIRTH_YEAR>
        <BIRTH_MONTH>  04 </BIRTH_MONTH>
        <BIRTH_DAY>    01 </BIRTH_DAY>
    </PERSON>
    <PERSON name="B">
        <BIRTH_YEAR> 1957 </BIRTH_YEAR>
        <BIRTH_MONTH>  08 </BIRTH_MONTH>
        <BIRTH_DAY>    29 </BIRTH_DAY>
    </PERSON>
    <PERSON name="C">
        <BIRTH_YEAR> 1802 </BIRTH_YEAR>
        <BIRTH_MONTH>  12 </BIRTH_MONTH>
        <BIRTH_DAY>    14 </BIRTH_DAY>
    </PERSON>
    <PERSON name="D">
        <BIRTH_YEAR> 2015 </BIRTH_YEAR>
        <BIRTH_MONTH>  04 </BIRTH_MONTH>
        <BIRTH_DAY>    30 </BIRTH_DAY>
    </PERSON>
</RECORDS>

并应用以下示例XSL片段:

<xsl:template match="PERSON">
    <!-- Copy element itself -->
    <xsl:copy>
        <!-- Copy all attributes -->
        <xsl:copy-of select="@*"/>
        <!-- Process birthdate info -->
        <BIRTH_DATE>
            <xsl:value-of select="normalize-space(BIRTH_MONTH)"/>
            <xsl:text>/</xsl:text>
            <xsl:value-of select="normalize-space(BIRTH_DAY)"/>
            <xsl:text>/</xsl:text>
            <xsl:value-of select="normalize-space(BIRTH_YEAR)"/>
        </BIRTH_DATE>
    </xsl:copy>
</xsl:template>

我们将获得此示例XML输出:

<PERSON name="A"><BIRTH_DATE>04/01/1943</BIRTH_DATE></PERSON>
<PERSON name="B"><BIRTH_DATE>08/29/1957</BIRTH_DATE></PERSON>
<PERSON name="C"><BIRTH_DATE>12/14/1802</BIRTH_DATE></PERSON>
<PERSON name="D"><BIRTH_DATE>04/30/2015</BIRTH_DATE></PERSON>

完整的XSLT表可以依次遍历每个<PERSON>元素,将<PERSON> XML结构传递给上面的模板。在单个<PERSON>结构的上下文中,只有一个<BIRTH_YEAR>元素,只有一个<BIRTH_MONTH>,只有一个<BIRTH_DAY>,因此我们可以安全地选择我们想要的内容通过指定名称。如果在单个父元素下存在多个这样的元素,事情会变得更复杂。 :)

以上测试适用于最常支持的XSLT 1.0版。

答案 1 :(得分:1)

我想知道你为什么不能做一些像:

<xsl:template match="BIRTH_YEAR">
    <BIRTH_DATE>
        <xsl:value-of select="following-sibling::*[1]"/>
        <xsl:text>/</xsl:text>
        <xsl:value-of select="following-sibling::*[2]"/>
        <xsl:text>/</xsl:text>
        <xsl:value-of select="."/>
    </BIRTH_DATE>
</xsl:template>

或者,如果您希望它更通用:

<xsl:template match="*[contains(name(), '_YEAR')]">
    <xsl:element name="{substring-before(name(), '_YEAR')}_DATE">
        <xsl:value-of select="following-sibling::*[1]"/>
        <xsl:text>/</xsl:text>
        <xsl:value-of select="following-sibling::*[2]"/>
        <xsl:text>/</xsl:text>
        <xsl:value-of select="."/>      
    </xsl:element>
</xsl:template>

答案 2 :(得分:0)

如果你想要一个不那么详细的解决方案,你可以替换

    <BIRTH_DATE>
        <xsl:value-of select="normalize-space(BIRTH_MONTH)"/>
        <xsl:text>/</xsl:text>
        <xsl:value-of select="normalize-space(BIRTH_DAY)"/>
        <xsl:text>/</xsl:text>
        <xsl:value-of select="normalize-space(BIRTH_YEAR)"/>
    </BIRTH_DATE>

<BIRTH_DATE>
  <xsl:value-of select="translate(normalize-space(
     concat(BIRTH_MONTH, ' ', BIRTH_DAY, ' ', BIRTH_YEAR)), ' ', '/')"/>
</BIRTH_DATE>

话虽如此,我很难编写代码来产生大多数非美国读者会误解的输出。你确定这是你想做的吗?

答案 3 :(得分:0)

以下是我提供给原始问题的答案,OP打算随时删除,所以我在这里提出答案。我没有涉及XSLT 3.0解决方案,请注意,动态XPath评估不是XSLT 3.0的强制功能,一些XSLT处理器可能选择不实现它。

  
<xsl:variable name="MM">
    <xsl:value-of select="concat('../', $which_date, '_MONTH')"/>
</xsl:variable>
     

以上返回值为../ BIRTH_MONTH。

你想要

<xsl:variable name="MM" select="../*[name()=concat($which_date, '_MONTH')]"/>

这是一个完整的转型

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>
 <xsl:param name="which_date" select="'BIRTH'"/>

  <xsl:template match="x">
    <xsl:variable name="MM" select=
         "../*[name()=concat($which_date, '_MONTH')]"/>
    <xsl:value-of select="$MM"/>
  </xsl:template>

  <xsl:template match="text()"/>
</xsl:stylesheet>

对以下XML文档应用此转换时(问题未提供):

<t>
 <x>1</x>
 <BIRTH_MONTH>12</BIRTH_MONTH>
</t>

产生了想要的正确结果

12

更新:根据您的其他类似问题,我们发现您需要参数化解决方案。

以下是一种可能的参数化解决方案:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:param name="pdateComponents" select="'|BIRTH_MONTH|BIRTH_DAY|BIRTH_YEAR|'"/>
 <xsl:param name="poutputDateName" select="'BIRTH_DATE'"/>

  <xsl:template match="/*">
    <xsl:apply-templates select=
     "*[*[contains($pdateComponents, concat('|',name(),'|'))]]" mode="dateHolder"/>
  </xsl:template>

  <xsl:template match="*" mode="dateHolder">
    <xsl:copy>
      <xsl:copy-of select="@*"/>
      <xsl:element name="{$poutputDateName}">
        <xsl:apply-templates select=
        "*[contains($pdateComponents, concat('|',name(),'|'))]" mode="date">
          <xsl:sort select="substring-before($pdateComponents, concat('|',name(),'|'))"/>
        </xsl:apply-templates>
      </xsl:element>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="*" mode="date">
    <xsl:value-of select=
         "concat(substring('/', 1 + (position() = 1)), normalize-space())"/>
  </xsl:template>
</xsl:stylesheet>

对以下XML文档应用此XSLT 1.0转换时

<RECORDS>
    <PERSON name="A">
        <BIRTH_YEAR> 1943 </BIRTH_YEAR>
        <BIRTH_MONTH>  04 </BIRTH_MONTH>
        <BIRTH_DAY>    01 </BIRTH_DAY>
    </PERSON>
    <PERSON name="B">
        <BIRTH_YEAR> 1957 </BIRTH_YEAR>
        <BIRTH_MONTH>  08 </BIRTH_MONTH>
        <BIRTH_DAY>    29 </BIRTH_DAY>
    </PERSON>
    <PERSON name="C">
        <BIRTH_YEAR> 1802 </BIRTH_YEAR>
        <BIRTH_MONTH>  12 </BIRTH_MONTH>
        <BIRTH_DAY>    14 </BIRTH_DAY>
    </PERSON>
    <PERSON name="D">
        <BIRTH_YEAR> 2015 </BIRTH_YEAR>
        <BIRTH_MONTH>  04 </BIRTH_MONTH>
        <BIRTH_DAY>    30 </BIRTH_DAY>
    </PERSON>
</RECORDS>

结果是

<PERSON name="A">
   <BIRTH_DATE>04/01/1943</BIRTH_DATE>
</PERSON>
<PERSON name="B">
   <BIRTH_DATE>08/29/1957</BIRTH_DATE>
</PERSON>
<PERSON name="C">
   <BIRTH_DATE>12/14/1802</BIRTH_DATE>
</PERSON>
<PERSON name="D">
   <BIRTH_DATE>04/30/2015</BIRTH_DATE>
</PERSON>

请注意

  1. 日期组件的名称在变换的全局参数中提供 - 它们不是静态知道的。
  2. 包含日期的输出元素的名称也作为转换的另一个全局参数的值提供。
  3. 在第一个组件中提供了用于构造日期的日期组件的顺序!
  4. 因此,上面提供的参数会导致美国日期输出。

    但是,如果我们提供此参数

     <xsl:param name="pdateComponents" select="'|BIRTH_DAY|BIRTH_MONTH|BIRTH_YEAR|'"/>
    

    然后转换的结果包含欧洲格式的日期:

    <PERSON name="A">
       <BIRTH_DATE>01/04/1943</BIRTH_DATE>
    </PERSON>
    <PERSON name="B">
       <BIRTH_DATE>29/08/1957</BIRTH_DATE>
    </PERSON>
    <PERSON name="C">
       <BIRTH_DATE>14/12/1802</BIRTH_DATE>
    </PERSON>
    <PERSON name="D">
       <BIRTH_DATE>30/04/2015</BIRTH_DATE>
    </PERSON>