【问题标题】:Is there a simple way to copy an XML docuement, but change the format of date nodes?是否有一种简单的方法可以复制 XML 文档,但更改日期节点的格式?
【发布时间】:2020-05-23 02:08:34
【问题描述】:

我的 XSLT 知识还很初级,但我已经了解了这么多。给定一组节点,我可以应用一个模板来选择节点,从而改变日期的格式。像这样的:

<?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>

【问题讨论】:

  • 您能否提供输入 XML 以制作minimal reproducible example
  • 嗯,部分原因是,传递给它的 XML 应该不是很重要。我可以使用身份转换复制任何 xml,对吗?我希望能够做类似的事情,但支持特殊情况。我已经添加了我正在使用的 XML。
  • 明确地说,我希望能够指定任意数量的节点,而不仅仅是一个。

标签: xml xslt-2.0


【解决方案1】:

如何将您的 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”的所有元素。

【讨论】:

  • 关闭,但这与我引用的答案存在相同的问题:我必须为要格式化的每个日期节点创建一个单独的模板。在我的示例中,“Worker/node()”将匹配 Worker 的任何子节点,因此要格式化每个不同的日期字段,我只需调用“" 对于给定的节点。
  • 嗯,是的,这就是 XSL 模板的作用。但是仍然没有出现将匹配元素与不匹配元素区分开来的模式。
  • 我不确定你的意思。我有一个匹配任何节点的模板。我有一个与 Worker 的任何孩子匹配的模板。我希望能够复制任何节点,除非它与 worker 的子节点匹配,然后命名应为该匹配选择的 Worker 的一些特定子节点。
  • 我知道所有的 PIECES 都是可能的,我只是不知道如何按照我的想法将它们组合在一起。这可能是不可能的。
  • 我更新了我的答案。从一般排除规则中排除您想要匹配的元素 - 就像上面的答案一样 - 然后结果应该没问题。
【解决方案2】:

作为对@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() 将结果格式化为两位数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-30
    • 1970-01-01
    • 1970-01-01
    • 2012-01-05
    • 1970-01-01
    • 2021-01-11
    相关资源
    最近更新 更多