XSLT转换日期时间为日期格式

5
我试图将日期时间转换为日期格式yyyy-MM-dd,因为我正在使用xsd.exe工具,而xs:date数据类型会自动更改为日期时间数据类型,因为在.NET Framework中没有完全匹配xs:date类型的类型。但是我无法使它正常工作。
<articles>
        <article>
          <articleid>48992</articleid>
          <deliverydateasked>2009-01-29T00:00:00+01:00</deliverydateasked>
        </article>
        <article>
          <articleid>48993</articleid>
          <deliverydateasked>2009-01-30T00:00:00+01:00</deliverydateasked>
        </article>
</articles>

尝试将XML转换为

<articles>
        <article>
          <articleid>48992</articleid>
          <deliverydateasked>2009-01-29</deliverydateasked>
        </article>
        <article>
          <articleid>48993</articleid>
          <deliverydateasked>2009-01-30</deliverydateasked>
        </article>
</articles>

目前我正在使用这个XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
    <articles>
        <xsl:apply-templates select="article">
        </xsl:apply-templates>
            </articles>
</xsl:template>

<xsl:template name="FormatDate">

    <xsl:param name="DateTime" />
    <xsl:variable name="date">
        <xsl:value-of select="substring-before($DateTime,'T')" />
    </xsl:variable>

    <xsl:if test="string-length($date) != 10">
        <xsl:value-of select="$DateTime"/>
    </xsl:if>
    <xsl:if test="string-length($date) = 10">
        <xsl:value-of select="$date"/>
    </xsl:if>
</xsl:template>

<xsl:template match="article">
        <xsl:call-template name="FormatDate">
            <xsl:with-param name="DateTime" select="deliverydateasked"/>
        </xsl:call-template>    
</xsl:template>     

有人知道一个好的XSLT转换吗?

提前感谢。

我的代码输出结果是:

<articles /> 
3个回答

8

在XPath 2.0中,格式化将变得更加容易,但Microsoft已经拒绝支持8年了。由于.NET中的格式问题只对XSLT有影响,因此我喜欢使用自定义函数,这样更加简洁和易于使用:

带有格式化函数的XSLT:

    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt"
    xmlns:user="http://www.tempuri.org/User">

  <msxsl:script implements-prefix="user" language="C#">
        <![CDATA[
          public string FormatCurrency(string amount)
          {
            return decimal.Parse(amount).ToString("C0");
          }

          public string FormatDate(string dateValue)
          {
            return DateTime.Parse(dateValue).ToString("MM/dd/yyyy hh:mm");
          }
          ]]>
      </msxsl:script>

使用方法:

<xsl:value-of select="user:FormatDate(@transactionDate)"/>
<xsl:value-of select="user:FormatCurrency(@amount)"/>

在使用.Net执行XSLT时,请确保告诉它是可信的(以便msxsl:script块能够运行)。
XslCompiledTransform.Load(reader, XsltSettings.TrustedXslt, null);

7

老实说,这对我来说看起来很合适 - 有时一个简单的子字符串就足够了。

但是,如果你在.NET领域,并且真的需要额外的功能,.NET有XSLT扩展对象


编辑:哦,我明白了,你有一个基本的应用模板概念问题。尝试使用以下内容(注意复制和根模板匹配):

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

<xsl:template match="*">
    <xsl:copy><xsl:apply-templates /></xsl:copy>
</xsl:template>

<xsl:template match="deliverydateasked">
    <xsl:copy>
        <xsl:call-template name="FormatDate">
            <xsl:with-param name="DateTime" select="."/>
        </xsl:call-template>    
    </xsl:copy>
</xsl:template>

<xsl:template name="FormatDate">

        <xsl:param name="DateTime" />
        <xsl:variable name="date">
                <xsl:value-of select="substring-before($DateTime,'T')" />
        </xsl:variable>

        <xsl:if test="string-length($date) != 10">
                <xsl:value-of select="$DateTime"/>
        </xsl:if>
        <xsl:if test="string-length($date) = 10">
                <xsl:value-of select="$date"/>
        </xsl:if>
</xsl:template>

</xsl:stylesheet>

模板是一个难以学习的概念,你最好从更直观的for-each开始学起,或者看一些XSLT教程/书籍似乎会更有帮助。


1
关于“使用更简单的for-each”:http://gregbeech.com/blogs/tech/archive/2006/08/17/using-xsl-for-each-is-almost-always-wrong.aspx。我赞成从一开始就正确地做。 :) - Tomalak
从我亲自教授XSLT给新手的经验来看,我强烈不同意这种观点——for-each是一个可翻译且易于理解的概念,如果有什么问题,它甚至可以作为解释模板概念的便捷跳板:“现在你已经明白了这个,看看这个能做什么!” - annakata
1
Tomalak提供的指向Greg Beech文章的链接已经更改;现在是http://gregbeech.com/blog/using-xsl-for-each-is-almost-always-wrong。 - Val

2
感谢Stesoc和annakata,我已经弄清楚了。 这是我现在正在使用的代码,它完美地工作。
<xsl:template match="*">
    <xsl:param name="parentElm">
        <xsl:value-of select="name(..)" />
    </xsl:param>
    <xsl:choose>
        <xsl:when test="local-name() = 'deliverydateasked'">
            <xsl:element name="deliverydateasked">
                <xsl:call-template name="FormatDate">
                    <xsl:with-param name="DateTime" select="."/>
                </xsl:call-template>
            </xsl:element>
        </xsl:when>
        <xsl:otherwise>
            <xsl:element name="{local-name()}">
                <xsl:copy-of select="@*" />
                <xsl:apply-templates select="@* | node()" />
            </xsl:element>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

<xsl:template name="FormatDate">
    <xsl:param name="DateTime" />
    <xsl:variable name="date">
        <xsl:value-of select="substring-before($DateTime,'T')" />
    </xsl:variable>

    <xsl:if test="string-length($date) != 10">
        <xsl:value-of select="$DateTime"/>
    </xsl:if>
    <xsl:if test="string-length($date) = 10">
        <xsl:value-of select="$date"/>
    </xsl:if>
</xsl:template>     


你为什么要做那些复杂的“local-name()”操作? - Tomalak
Tomalak所说的-利用xsl:copy - annakata
Annakata,我尝试了你的第一个建议<xsl:template match="*">xsl:copy<xsl:apply-templates /></xsl:copy></xsl:template>,结果是:<articles>2009-01-292009-01-30</articles>。或者是我的操作有误?但是,local-name()是否作为一个避免函数更好?现在持续时间为15-30毫秒。 - freggel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接