有没有一种简单的方法可以复制 XML 文档,但更改日期节点的格式?

Is there a simple way to copy an XML docuement, but change the format of date nodes?

我的 XSLT 知识还很初级,但我已经了解了这么多。给定一组节点,我可以将模板应用于 select 个节点,这会更改日期格式。像这样:

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

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

    <xsl:template match="Worker">
        <xsl:value-of select="LINE_NO"/>
        <xsl:text>,</xsl:text>
        <xsl:value-of select="WD_BATCH_ID"/>
        <xsl:text>,</xsl:text>
        <xsl:apply-templates select="./DATE_WORKED"/>
        <xsl:text>,</xsl:text>
        <xsl:apply-templates select="./EFFECTIVE_DATE"/>
        <xsl:text>,</xsl:text>
        <xsl:value-of select="HOURS"/>
        <xsl:text>
</xsl:text>
    </xsl:template>

    <xsl:template match="Worker/node()">
        <xsl:value-of select="concat('20',substring-after(substring-after(.,'-'),'-'))"/>
        <xsl:text>-</xsl:text>
        <xsl:choose>
            <xsl:when test="contains(., 'JAN')">01</xsl:when>
            <xsl:when test="contains(., 'FEB')">02</xsl:when>
            <xsl:when test="contains(., 'MAR')">03</xsl:when>
            <xsl:when test="contains(., 'APR')">04</xsl:when>
            <xsl:when test="contains(., 'MAY')">05</xsl:when>
            <xsl:when test="contains(., 'JUN')">06</xsl:when>
            <xsl:when test="contains(., 'JUL')">07</xsl:when>
            <xsl:when test="contains(., 'AUG')">08</xsl:when>
            <xsl:when test="contains(., 'SEP')">09</xsl:when>
            <xsl:when test="contains(., 'OCT')">10</xsl:when>
            <xsl:when test="contains(., 'NOV')">11</xsl:when>
            <xsl:when test="contains(., 'DEC')">12</xsl:when>
            <xsl:otherwise></xsl:otherwise>
        </xsl:choose>
        <xsl:text>-</xsl:text>
        <xsl:value-of select="substring-before(.,'-')"/>
    </xsl:template>
</xsl:stylesheet>

这个 XSLT 代表了我对 XSLT 理解的一些突破,所以我为此感到有点自豪 ;-) 我喜欢这样的事实,我可以将日期格式模板应用于 Worker,我所要做的就是命名子节点。这个恰好产生一个 CSV 文件。

我想做类似的事情,但是复制 XML,并且不必指定要复制的每个节点,只有我想要的节点具有此日期格式模板。

是否有一种方便的方法可以不可知地说“复制此 XML 中的任何给定节点,如果节点名为 DATE_WORKED,则在复制时应用此模板?或者,如果有一些XSL 可以神奇地识别任何日期并对其进行格式化的方式……这很酷。

我可以访问 XSL 1、2 和 3.0

我心里有这样的想法:

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

    <xsl:template match="/ | @* | node()">
        <xsl:copy>
             <xsl:apply-templates select="//DATE_WORKED" />
        </xsl:copy>
    </xsl:template>

    <xsl:template match="Worker/node()">
        <xsl:value-of select="concat('20',substring-after(substring-after(.,'-'),'-'))"/>
        <xsl:text>-</xsl:text>
        <xsl:choose>
            <xsl:when test="contains(., 'JAN')">01</xsl:when>
            <xsl:when test="contains(., 'FEB')">02</xsl:when>
            <xsl:when test="contains(., 'MAR')">03</xsl:when>
            <xsl:when test="contains(., 'APR')">04</xsl:when>
            <xsl:when test="contains(., 'MAY')">05</xsl:when>
            <xsl:when test="contains(., 'JUN')">06</xsl:when>
            <xsl:when test="contains(., 'JUL')">07</xsl:when>
            <xsl:when test="contains(., 'AUG')">08</xsl:when>
            <xsl:when test="contains(., 'SEP')">09</xsl:when>
            <xsl:when test="contains(., 'OCT')">10</xsl:when>
            <xsl:when test="contains(., 'NOV')">11</xsl:when>
            <xsl:when test="contains(., 'DEC')">12</xsl:when>
            <xsl:otherwise></xsl:otherwise>
        </xsl:choose>
        <xsl:text>-</xsl:text>
        <xsl:value-of select="substring-before(.,'-')"/>
    </xsl:template>

</xsl:stylesheet>

...不复制节点,仅在 "DATE_WORKED" 个节点上运行。

是的,我找到了这个答案:Using XSLT to copy all nodes in XML, with support for special cases但是它需要为我想要格式化的每个节点制作一个重复的模板。

我正在使用看起来像这样的 XML:

<?xml version='1.0' encoding='UTF-8'?>
<Data>
    <Worker>
        <LINE_NO>LineNo</LINE_NO>
        <INT_ID>IntId</INT_ID>
        <WD_BATCH_ID>WdBatchId</WD_BATCH_ID>
        <WD_PAY_INPUT_ID>WdPayInputId</WD_PAY_INPUT_ID>
        <DATE_WORKED>DateWorked</DATE_WORKED>
        <EMPLOYEE_ID>EmployeeId</EMPLOYEE_ID>
        <WEEK_END_DATE>WeekEndDate</WEEK_END_DATE>
        <EFFECTIVE_DATE>EffectiveDate</EFFECTIVE_DATE>
        <HOURS>Hours</HOURS>
        <PAY_COMPONENT>PayComponent</PAY_COMPONENT>
        <TK_COMMENTS>TkComments</TK_COMMENTS>
        <SS_REQUEST_ID>SsRequestId</SS_REQUEST_ID>
        <REQUEST_ID>RequestId</REQUEST_ID>
        <PROCESS_STATUS>ProcessStatus</PROCESS_STATUS>
    </Worker>
    <Worker>
        <LINE_NO>0001</LINE_NO>
        <INT_ID>248697</INT_ID>
        <WD_BATCH_ID>kns_timeoff20191215_7_78593974</WD_BATCH_ID>
        <WD_PAY_INPUT_ID>248696</WD_PAY_INPUT_ID>
        <DATE_WORKED>25-NOV-19</DATE_WORKED>
        <EMPLOYEE_ID>101877</EMPLOYEE_ID>
        <WEEK_END_DATE>01-DEC-19</WEEK_END_DATE>
        <EFFECTIVE_DATE>15-DEC-19</EFFECTIVE_DATE>
        <HOURS>9</HOURS>
        <PAY_COMPONENT>Military AD</PAY_COMPONENT>
        <TK_COMMENTS>.</TK_COMMENTS>
        <SS_REQUEST_ID>78593755</SS_REQUEST_ID>
        <REQUEST_ID>78593974</REQUEST_ID>
        <PROCESS_STATUS>C</PROCESS_STATUS>
    </Worker>
    <Worker>
        <LINE_NO>0002</LINE_NO>
        <INT_ID>248701</INT_ID>
        <WD_BATCH_ID>kns_timeoff20191215_7_78593974</WD_BATCH_ID>
        <WD_PAY_INPUT_ID>248700</WD_PAY_INPUT_ID>
        <DATE_WORKED>26-NOV-19</DATE_WORKED>
        <EMPLOYEE_ID>101877</EMPLOYEE_ID>
        <WEEK_END_DATE>01-DEC-19</WEEK_END_DATE>
        <EFFECTIVE_DATE>15-DEC-19</EFFECTIVE_DATE>
        <HOURS>9</HOURS>
        <PAY_COMPONENT>Military AD</PAY_COMPONENT>
        <TK_COMMENTS>.</TK_COMMENTS>
        <SS_REQUEST_ID>78593755</SS_REQUEST_ID>
        <REQUEST_ID>78593974</REQUEST_ID>
        <PROCESS_STATUS>C</PROCESS_STATUS>
    </Worker>
    <Worker>
        <LINE_NO>0003</LINE_NO>
        <INT_ID>248699</INT_ID>
        <WD_BATCH_ID>kns_timeoff20191215_7_78593974</WD_BATCH_ID>
        <WD_PAY_INPUT_ID>248698</WD_PAY_INPUT_ID>
        <DATE_WORKED>27-NOV-19</DATE_WORKED>
        <EMPLOYEE_ID>101877</EMPLOYEE_ID>
        <WEEK_END_DATE>01-DEC-19</WEEK_END_DATE>
        <EFFECTIVE_DATE>15-DEC-19</EFFECTIVE_DATE>
        <HOURS>9</HOURS>
        <PAY_COMPONENT>Military AD</PAY_COMPONENT>
        <TK_COMMENTS>.</TK_COMMENTS>
        <SS_REQUEST_ID>78593755</SS_REQUEST_ID>
        <REQUEST_ID>78593974</REQUEST_ID>
        <PROCESS_STATUS>C</PROCESS_STATUS>
    </Worker>
    <Worker>
        <LINE_NO>0004</LINE_NO>
        <INT_ID>1082611</INT_ID>
        <WD_BATCH_ID>kns_wd20191215_8_78593974</WD_BATCH_ID>
        <WD_PAY_INPUT_ID>WK1.1082611</WD_PAY_INPUT_ID>
        <DATE_WORKED>01-DEC-19</DATE_WORKED>
        <EMPLOYEE_ID>101877</EMPLOYEE_ID>
        <WEEK_END_DATE>01-DEC-19</WEEK_END_DATE>
        <EFFECTIVE_DATE>15-DEC-19</EFFECTIVE_DATE>
        <HOURS>7</HOURS>
        <PAY_COMPONENT>EWWHRWK1</PAY_COMPONENT>
        <TK_COMMENTS>.</TK_COMMENTS>
        <SS_REQUEST_ID>78593755</SS_REQUEST_ID>
        <REQUEST_ID>78593974</REQUEST_ID>
        <PROCESS_STATUS>C</PROCESS_STATUS>
    </Worker>
</Data>

如何将 XSLT 代码更改为

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

    <!-- Remove all space between * elements -->
    <xsl:strip-space elements="*" />

    <!-- Identity template -->
    <xsl:template match="node()|@*">
        <xsl:copy>
             <xsl:apply-templates select="node()|@*" />
        </xsl:copy>
    </xsl:template>

    <!-- Except for DATE_WORKED or WEEK_END_DATE or EFFECTIVE_DATE children -->
    <xsl:template match="DATE_WORKED | WEEK_END_DATE | EFFECTIVE_DATE">
        <xsl:copy>
            <xsl:value-of select="concat('20',substring-after(substring-after(.,'-'),'-'))"/>
            <xsl:text>-</xsl:text>
            <xsl:choose>
                <xsl:when test="contains(., 'JAN')">01</xsl:when>
                <xsl:when test="contains(., 'FEB')">02</xsl:when>
                <xsl:when test="contains(., 'MAR')">03</xsl:when>
                <xsl:when test="contains(., 'APR')">04</xsl:when>
                <xsl:when test="contains(., 'MAY')">05</xsl:when>
                <xsl:when test="contains(., 'JUN')">06</xsl:when>
                <xsl:when test="contains(., 'JUL')">07</xsl:when>
                <xsl:when test="contains(., 'AUG')">08</xsl:when>
                <xsl:when test="contains(., 'SEP')">09</xsl:when>
                <xsl:when test="contains(., 'OCT')">10</xsl:when>
                <xsl:when test="contains(., 'NOV')">11</xsl:when>
                <xsl:when test="contains(., 'DEC')">12</xsl:when>
                <xsl:otherwise></xsl:otherwise>
            </xsl:choose>
            <xsl:text>-</xsl:text>
            <xsl:value-of select="substring-before(.,'-')"/>
        </xsl:copy>
    </xsl:template>

它的输出(例如)是:

<?xml version="1.0" encoding="UTF-8"?>
<Data>
    <Worker>
        <LINE_NO>LineNo</LINE_NO>
        <INT_ID>IntId</INT_ID>
        <WD_BATCH_ID>WdBatchId</WD_BATCH_ID>
        <WD_PAY_INPUT_ID>WdPayInputId</WD_PAY_INPUT_ID>
        <DATE_WORKED>20--</DATE_WORKED>
        <EMPLOYEE_ID>EmployeeId</EMPLOYEE_ID>
        <WEEK_END_DATE>20--</WEEK_END_DATE>
        <EFFECTIVE_DATE>20--</EFFECTIVE_DATE>
        <HOURS>Hours</HOURS>
        <PAY_COMPONENT>PayComponent</PAY_COMPONENT>
        <TK_COMMENTS>TkComments</TK_COMMENTS>
        <SS_REQUEST_ID>SsRequestId</SS_REQUEST_ID>
        <REQUEST_ID>RequestId</REQUEST_ID>
        <PROCESS_STATUS>ProcessStatus</PROCESS_STATUS>
    </Worker>
    <Worker>
        <LINE_NO>0001</LINE_NO>
        <INT_ID>248697</INT_ID>
        <WD_BATCH_ID>kns_timeoff20191215_7_78593974</WD_BATCH_ID>
        <WD_PAY_INPUT_ID>248696</WD_PAY_INPUT_ID>
        <DATE_WORKED>2019-11-25</DATE_WORKED>
        <EMPLOYEE_ID>101877</EMPLOYEE_ID>
        <WEEK_END_DATE>2019-12-01</WEEK_END_DATE>
        <EFFECTIVE_DATE>2019-12-15</EFFECTIVE_DATE>
        <HOURS>9</HOURS>
        <PAY_COMPONENT>Military AD</PAY_COMPONENT>
        <TK_COMMENTS>.</TK_COMMENTS>
        <SS_REQUEST_ID>78593755</SS_REQUEST_ID>
        <REQUEST_ID>78593974</REQUEST_ID>
        <PROCESS_STATUS>C</PROCESS_STATUS>
    </Worker>
    <Worker>
        <LINE_NO>0002</LINE_NO>
        <INT_ID>248701</INT_ID>
        <WD_BATCH_ID>kns_timeoff20191215_7_78593974</WD_BATCH_ID>
        <WD_PAY_INPUT_ID>248700</WD_PAY_INPUT_ID>
        <DATE_WORKED>2019-11-26</DATE_WORKED>
        <EMPLOYEE_ID>101877</EMPLOYEE_ID>
        <WEEK_END_DATE>2019-12-01</WEEK_END_DATE>
        <EFFECTIVE_DATE>2019-12-15</EFFECTIVE_DATE>
        <HOURS>9</HOURS>
        <PAY_COMPONENT>Military AD</PAY_COMPONENT>
        <TK_COMMENTS>.</TK_COMMENTS>
        <SS_REQUEST_ID>78593755</SS_REQUEST_ID>
        <REQUEST_ID>78593974</REQUEST_ID>
        <PROCESS_STATUS>C</PROCESS_STATUS>
    </Worker>
    <Worker>
        <LINE_NO>0003</LINE_NO>
        <INT_ID>248699</INT_ID>
        <WD_BATCH_ID>kns_timeoff20191215_7_78593974</WD_BATCH_ID>
        <WD_PAY_INPUT_ID>248698</WD_PAY_INPUT_ID>
        <DATE_WORKED>2019-11-27</DATE_WORKED>
        <EMPLOYEE_ID>101877</EMPLOYEE_ID>
        <WEEK_END_DATE>2019-12-01</WEEK_END_DATE>
        <EFFECTIVE_DATE>2019-12-15</EFFECTIVE_DATE>
        <HOURS>9</HOURS>
        <PAY_COMPONENT>Military AD</PAY_COMPONENT>
        <TK_COMMENTS>.</TK_COMMENTS>
        <SS_REQUEST_ID>78593755</SS_REQUEST_ID>
        <REQUEST_ID>78593974</REQUEST_ID>
        <PROCESS_STATUS>C</PROCESS_STATUS>
    </Worker>
    <Worker>
        <LINE_NO>0004</LINE_NO>
        <INT_ID>1082611</INT_ID>
        <WD_BATCH_ID>kns_wd20191215_8_78593974</WD_BATCH_ID>
        <WD_PAY_INPUT_ID>WK1.1082611</WD_PAY_INPUT_ID>
        <DATE_WORKED>2019-12-01</DATE_WORKED>
        <EMPLOYEE_ID>101877</EMPLOYEE_ID>
        <WEEK_END_DATE>2019-12-01</WEEK_END_DATE>
        <EFFECTIVE_DATE>2019-12-15</EFFECTIVE_DATE>
        <HOURS>7</HOURS>
        <PAY_COMPONENT>EWWHRWK1</PAY_COMPONENT>
        <TK_COMMENTS>.</TK_COMMENTS>
        <SS_REQUEST_ID>78593755</SS_REQUEST_ID>
        <REQUEST_ID>78593974</REQUEST_ID>
        <PROCESS_STATUS>C</PROCESS_STATUS>
    </Worker>
</Data>

第一项的输出是错误的,所以你需要做一个例外规则。但其余的确实适合。

此外,在 XSLT-3.0 中,身份模板 可以替换为元素

<xsl:mode on-no-match="shallow-copy" />

为了使其更通用,您可以使用模板匹配规则

<xsl:template match="*[contains(local-name(),'DATE')]">

匹配名称中包含字符串 'DATE'.

的所有元素

补充@zx485的回答,如果你想避免丑xsl:choose,你可以试试

<xsl:function name="f:month-number" as="xs:integer?">
  <xsl:param name="month-abbr" as="xs:string"/>
  <xsl:sequence select="index-of(('JAN', 'FEB', 'MAR', ...), $month-abbr)"/>
</xsl:function>

然后使用 format-integer() 将结果格式化为两位数。