Есть ли простой способ скопировать документ XML, но изменить формат узлов даты? - PullRequest
0 голосов
/ 07 февраля 2020

Мои знания по 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".

Да, я нашел такой ответ: Использование XSLT для копирования всех узлов в XML с поддержкой особых случаев Но для каждого узла, который я хочу отформатировать, потребуется создать дубликат шаблона.

Я работаю с 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>

Ответы [ 2 ]

1 голос
/ 07 февраля 2020

В дополнение к ответу от @ 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 (), чтобы отформатировать результат как два цифры.

1 голос
/ 07 февраля 2020

Как насчет изменения кода 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 шаблон Identity можно заменить элементом

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

И чтобы сделать его еще более общим, вы можете использовать сопоставление с шаблоном правило

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

, которое соответствует всем элементам, имена которых содержат строку 'DATE'.

...