Mi conocimiento de XSLT sigue siendo rudimentario, pero ya lo he descubierto. Dado un conjunto de nodos, puedo aplicar una plantilla para seleccionar nodos, que cambia el formato de las fechas. Algo como esto:

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

Este XSLT representa un pequeño avance en mi comprensión de XSLT, así que estoy un poco orgulloso de ello ;-) Me gusta el hecho de que puedo aplicar la plantilla de formato de fecha a cualquier hijo directo de Worker, y todo lo que tengo que hacer es nombrar el nodo hijo. Este produce un archivo CSV.

Me gustaría hacer algo similar, pero copie el XML, y sin tener que especificar cada nodo que se copia, solo los que quiero tener esta plantilla de formato de fecha.

¿Hay una manera conveniente de decir agnósticamente "copiar cualquier nodo dado en este XML, y si un nodo se llama DATE_WORKED, aplicar esta plantilla cuando la copie? Alternativamente, si hay alguna forma en que XSL pueda identificar mágicamente cualquier fecha y simplemente formatearlo ... sería genial.

Tengo acceso a XSL 1, 2 y 3.0

Tengo en mi mente algo como:

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

... que no copia los nodos, y solo opera en los nodos "DATE_WORKED".

Sí, he encontrado esta respuesta: Usar XSLT para copiar todos los nodos en XML, con soporte para casos especiales Pero requeriría hacer una plantilla duplicada para cada nodo que quiero formatear.

Estoy trabajando con XML que se ve así:

<?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>
0
Matt 7 feb. 2020 a las 02:16

2 respuestas

La mejor respuesta

¿Qué hay de cambiar su código XSLT a

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

Su salida (por ejemplo) es:

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

El resultado del primer elemento es incorrecto, por lo que debe hacer una regla de excepción. Pero el resto encaja.

Además, en XSLT-3.0, la plantilla de identidad puede reemplazarse por el elemento

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

Y para hacerlo aún más general, puede usar la regla de coincidencia de plantillas

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

Que coincide con todos los elementos cuyos nombres contienen la cadena 'DATE'.

1
zx485 7 feb. 2020 a las 00:31

Como complemento a la respuesta de @ zx485, si desea evitar lo feo xsl:choose, puede intentar

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

Y luego use format-integer () para formatear el resultado como dos dígitos.

1
Michael Kay 7 feb. 2020 a las 08:58