XSLT для каждой буквы в строке
Я пишу преобразование XSLT (для XSL-FO) и должен повторить что-то для каждой буквы в строковом значении, например:
если строка хранится в MyData/MyValue
string (например, MyData.MyValue = "что-то"), мне нужно для-каждого, как этот:
<xsl:for-each select="MyData/MyValue"> <!-- What goes here to iterate through letters? -->
<someTags>
<xsl:value-of select="Letter" /> <!-- What goes here to output current letter? -->
</someTags>
</xsl:for-each>
какие идеи?
6 ответов
вы можете использовать шаблон вызова и передачи параметров, а затем использовать рекурсию для вызова шаблона, пока не осталось символов.
пример добавил ниже.
на этом xml
<?xml version="1.0" encoding="utf-8"?>
<data>
<node>something</node>
</data>
и это xslt
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="data/node">
<xsl:call-template name="for-each-character">
<xsl:with-param name="data" select="."/>
</xsl:call-template>
</xsl:template>
<xsl:template name="for-each-character">
<xsl:param name="data"/>
<xsl:if test="string-length($data) > 0">
<someTags>
<xsl:value-of select="substring($data,1,1)"/>
</someTags>
<xsl:call-template name="for-each-character">
<xsl:with-param name="data" select="substring($data,2)"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
затем вы сможете манипулировать в операторе if, чтобы делать дальнейшие вещи!
вы можете попробовать этот грязный уродливый хак, который доказал, что работает снова и снова:
<xsl:for-each select="//*[position() <= string-length(MyData/MyValue)]">
<someTags>
<xsl:value-of select="substring(MyData/MyValue, position(), 1)"/>
</someTags>
</xsl:for-each>
это будет работать, если //*
матчи больше узлов, чем количество символов в строке... Конечно, это также заслуживает странной строки комментария для бедняги, читающего ваш код впоследствии... ;-)
Примечание: Я знаю, что есть пуристы XSLT. Но когда вам нужно выполнить работу и не заботиться о гипер-многословности XSLT, тогда иногда эти трюки потрясающие! ИМО
Отметим также,: Я поднял здесь вопрос о производительности, чтобы увидеть, работает ли итерация или рекурсия лучше:производительность итерации или рекурсии XSLT
Я не уверен в целесообразности итерации. Вы можете использовать рекурсию obsviously, как показано в других ответах. Это мое предложение (не сильно отличается от других, кроме того, что я использую шаблоны шаблонов, а не именованные шаблоны):
<xsl:template match="MyData/MyValue">
<xsl:param name="sub" select="."/>
<xsl:variable name="subsub" select="substring($sub,1,1)"/>
<xsl:if test="boolean($subsub)">
<someTags>
<xsl:value-of select="$subsub"/>
</someTags>
<xsl:apply-templates select="self::node()">
<xsl:with-param name="sub" select="substring($sub,2)"/>
</xsl:apply-templates>
</xsl:if>
</xsl:template>
Вы можете использовать рекурсию:
<?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="/">
<xsl:call-template name="get-letters">
<xsl:with-param name="input">something</xsl:with-param>
</xsl:call-template>
</xsl:template>
<xsl:template name="get-letters">
<xsl:param name="input"/>
<xsl:if test="string-length($input)">
<xsl:value-of select="substring($input, 1, 1)"/>
<xsl:text> </xsl:text>
<xsl:call-template name="get-letters">
<xsl:with-param name="input" select="substring($input, 2)"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
выход:
s o m e t h i n g
хороший вопрос, +1.
это то, что шаблон / функция str-map
С FXSL на:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:f="http://fxsl.sf.net/" xmlns:testmap="testmap"
exclude-result-prefixes="xsl f testmap">
<xsl:import href="str-dvc-map.xsl"/>
<!-- to be applied on any xml source -->
<testmap:testmap/>
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:variable name="vFunTripple" select="document('')/*/testmap:*[1]"/>
<xsl:call-template name="str-map">
<xsl:with-param name="pFun" select="$vFunTripple"/>
<xsl:with-param name="pStr" select="'something'"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="trippleChar" match="testmap:*" mode="f:FXSL">
<xsl:param name="arg1"/>
<xsl:value-of select="concat($arg1,$arg1,$arg1)"/>
</xsl:template>
</xsl:stylesheet>
когда это преобразование применяется к любому XML-документу (не используется), желаемый результат создается:
sssooommmeeettthhhiiinnnggg
было опубликовано несколько решений XSLT 1.0 (так как это то, что нужно оригинальному плакату). Для сравнения, вот как это можно сделать в XSLT 2.0 с помощью xsl: analyze-string:
<xsl:analyze-string select="MyData/MyValue" regex=".">
<xsl:matching-substring>
<someTags>
<xsl:value-of select="."/>
</someTags>
</xsl:matching-substring>
</xsl:analyze-string>