Формат даты XSLT для атрибута

<?xml version="1.0" encoding="UTF-8"?>
<catalog>
  <cd created_at="2016-12-15T15:02:55Z">
  <title created_at="2016-12-15T15:02:55Z">Empire Burlesque</title>
  <artist created_at="2016-12-15T15:02:55Z">Bob Dylan</artist>
  <cover created_at="2016-12-15T15:02:55Z"/>
  <company>Columbia</company>
  <price>10.90</price>
  <year>1985</year>
</cd>

Я хочу отформатировать все вхождения атрибута creation_at

input format YYYY-MM-DDTHH:MM:SSZ
output format YYYY-MM-DD HH:MM:SS

Я в настоящее время использую этот следующий 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="xml" indent="yes"/>

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

<!-- Edit dates to conform to dbunit format-->
<xsl:template match="@created_at">
    <xsl:copy>
        <xsl:call-template name="formatdate">
            <xsl:with-param name="datestr" select="@created_at"/>
        </xsl:call-template>
    </xsl:copy>
</xsl:template>


<xsl:template name="formatdate">
    <xsl:param name="datestr" />
    <!-- input format YYYY-MM-DDTHH:MM:SSZ -->
    <!-- output format YYYY-MM-DD HH:MM:SS -->

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

    <xsl:variable name="timetext">
        <xsl:value-of select="substring($datestr,12,18)" />
    </xsl:variable>

    <xsl:value-of select="concat($datetext, ' ', $timetext)" />
</xsl:template>
</xsl:stylesheet>

Однако, поскольку я отлаживаю через преобразование xslt, кажется, что он не вводит шаблон вызова formatdate. Мой xpath неправильный? Я нашел статьи об изменении узла, но не атрибута. Любая помощь приветствуется.

Спасибо

3 ответа

Почему бы просто:

<xsl:template match="@created_at">
    <xsl:attribute name="created_at">
        <xsl:value-of select="substring(translate(., 'T', ' '), 1, 19)" />
    </xsl:attribute>
</xsl:template>

Примечание: вы не можете использовать xsl:copy если вы хотите изменить значение атрибута.

Из вашего поста кажется, что все, что вам нужно, это простая обработка строк.

Почему ваш код работает не так, как вы хотите

Вы обращаетесь с @created_at атрибуты с этим шаблоном:

<xsl:template match="@created_at">
    <xsl:copy>
        <xsl:call-template name="formatdate">
            <xsl:with-param name="datestr" select="@created_at"/>
        </xsl:call-template>
    </xsl:copy>
</xsl:template>

Кикер здесь то, что вы используете <xsl:copy>, Когда используется с атрибутами, <xsl:copy> копирует весь атрибут, имя и значение обоих. А поскольку атрибуты не могут содержать детей, дети вашего <xsl:copy> инструкции игнорируются - поэтому процессор XSLT никогда не оценивает <xsl:call-template name="formatdate"> инструкция.

Другой подход, который работает

Вместо того, чтобы использовать <xsl:copy>, вам нужно вместо этого использовать <xsl:attribute> создать атрибут таким образом, чтобы вы также могли указать значение. В этом случае вы уже знаете имя атрибута, который хотите создать, поэтому вы можете жестко закодировать значение имени как created_at, Для более гибкого подхода вы могли бы вместо этого дать значение имени как {name(.)} - это просто захватывает имя обрабатываемого атрибута, которое ближе по поведению к тому, что вы, вероятно, думали <xsl:copy> сделал бы.:)

Также возможно произвести желаемую строку в одном xsl:value-of выражение, не полагаясь на так много переменных.

<xsl:template match="@created_at">
    <xsl:attribute name="{name(.)}">
        <xsl:value-of select="concat(substring-before(., 'T'), ' ', substring-before(substring-after(., 'T'), 'Z'))"/>
    </xsl:attribute>
</xsl:template>

Ломая это select заявление:

  • использование concat() сшить несколько битов строки.
  • использование substring-before(., 'T') захватить все до T - это часть даты.
  • ' ' добавляет один пробел в середине.
  • substring-before(substring-after(., 'T'), 'Z') -
    • Внутреннее выражение substring-after(., 'T') захватывает все после T - это часть времени.
    • Тем не менее, есть что надоедливый Z в конце концов, поэтому мы используем substring-before как внешнее выражение, чтобы отрубить это.

Нет необходимости в переменных, и он выполняет свою работу. Подтверждено для работы с XSLT 1.0.

Попробуй это

<xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
<xsl:output method='xml' indent='yes'/>

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

<!-- Edit dates to conform to dbunit format-->
<xsl:template match="@created_at">    
    <xsl:call-template name="formatdate">
        <xsl:with-param name="datestr" select="."/>
    </xsl:call-template>    
</xsl:template>    

<xsl:template name="formatdate">
    <xsl:param name="datestr" />
    <!-- input format YYYY-MM-DDTHH:MM:SSZ -->
    <!-- output format YYYY-MM-DD HH:MM:SS -->

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

    <xsl:variable name="timetext">
        <xsl:value-of select="substring($datestr,12,8)" />
    </xsl:variable>

    <xsl:attribute name="created_at">
        <xsl:value-of select="concat($datetext, ' ', $timetext)" />
    </xsl:attribute>
</xsl:template>

</xsl:stylesheet>
Другие вопросы по тегам