XSLT сравнивает числа как строки
Фон
Недавно я с удивлением заметил, что XSL был в состоянии разумно обрабатывать числа; т.е. умение обращаться с числами в тексте как с числовыми при выполнении сравнений (т.е. 7 < 10
вместо того, чтобы думать '10' < '7'
). В моем случае это то, что я хотел; только не то, что я ожидал.
Из любопытства я попытался заставить XSLT сравнивать числа в виде строк (т. Е. Используя string()
функционировать, но без удачи.
Вопрос
Можно ли заставить XSLT сравнивать числа в виде строк; например, так '10' < '7'
?
пример
Исходный XML:
<?xml version="1.0" encoding="utf-8"?>
<element>
<x>1</x>
<x>2</x>
<x>3</x>
<x>4</x>
<x>5</x>
<x>6</x>
<x>7</x>
<x>8</x>
<x>9</x>
<x>10</x>
</element>
XSLT:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output indent="yes"/>
<xsl:template match="element">
<element>
<AsItComes>
<xsl:for-each select="./x">
<xsl:if test="./text() < 7">
<xsl:copy-of select="."></xsl:copy-of>
</xsl:if>
</xsl:for-each>
</AsItComes>
<AsNumber>
<xsl:for-each select="./x">
<xsl:if test="number(./text()) < 7">
<xsl:copy-of select="."></xsl:copy-of>
</xsl:if>
</xsl:for-each>
</AsNumber>
<AsString>
<xsl:for-each select="./x">
<xsl:if test="string(./text()) < '7'">
<xsl:copy-of select="."></xsl:copy-of>
</xsl:if>
</xsl:for-each>
</AsString>
</element>
</xsl:template>
</xsl:stylesheet>
Ожидаемый результат:
<?xml version="1.0" encoding="utf-8"?>
<element>
<AsItComes>
<x>1</x>
<x>2</x>
<x>3</x>
<x>4</x>
<x>5</x>
<x>6</x>
<x>10</x>
</AsItComes>
<AsNumber>
<x>1</x>
<x>2</x>
<x>3</x>
<x>4</x>
<x>5</x>
<x>6</x>
</AsNumber>
<AsString>
<x>1</x>
<x>2</x>
<x>3</x>
<x>4</x>
<x>5</x>
<x>6</x>
<x>10</x>
</AsString>
</element>
Фактический выход:
<?xml version="1.0" encoding="utf-8"?>
<element>
<AsItComes>
<x>1</x>
<x>2</x>
<x>3</x>
<x>4</x>
<x>5</x>
<x>6</x>
</AsItComes>
<AsNumber>
<x>1</x>
<x>2</x>
<x>3</x>
<x>4</x>
<x>5</x>
<x>6</x>
</AsNumber>
<AsString>
<x>1</x>
<x>2</x>
<x>3</x>
<x>4</x>
<x>5</x>
<x>6</x>
</AsString>
</element>
3 ответа
Похоже, что в XSLT/XPATH 1.0string()
значение по-прежнему оценивается как число при выполнении сравнения.
Когда ни один объект для сравнения не является набором узлов и оператор <=, <,>= или>, тогда объекты сравниваются путем преобразования обоих объектов в числа и сравнения чисел в соответствии с IEEE 754. Сравнение<будет истина тогда и только тогда, когда первое число меньше второго числа.
С XSLT / XPATH 2.0 (и 3.0, и 3.1) вы можете явно установить тип данных как xs:string
чтобы гарантировать, что сравнение выполняется со строками и не приведено к числовым значениям.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
version="2.0">
<xsl:template match="element">
<element>
<AsString>
<xsl:for-each select="./x">
<xsl:if test="xs:string(.) < xs:string('7')">
<xsl:copy-of select="."></xsl:copy-of>
</xsl:if>
</xsl:for-each>
</AsString>
</element>
</xsl:template>
</xsl:stylesheet>
Но достаточно сравнить значение со строкой '7'
(Кроме того, вы могли бы устранить <xsl:if>
и поместите свой фильтр в предикат)
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:template match="element">
<element>
<AsString>
<xsl:for-each select="./x[. < '7']">
<xsl:copy-of select="."></xsl:copy-of>
</xsl:for-each>
</AsString>
</element>
</xsl:template>
</xsl:stylesheet>
Если вы выбираете первое число, то обходным путем может быть просто подстановка первой позиции.
<xsl:if test="substring(./text(), 1, 1) < '7'">
возвращается
<AsString>
<x>1</x>
<x>2</x>
<x>3</x>
<x>4</x>
<x>5</x>
<x>6</x>
<x>10</x>
</AsString>
Обратите внимание, что в XSLT 1.0 оба 'a' > 'b'
а также 'b' > 'a'
оценивать как false
,