<ROOT>
<A>
<B>TESTING</B>
</A>
</ROOT>
XSL:
<xsl:variable name="nodestring" select="//A"/>
<xsl:value-of select="$nodestring"/>
Я пытаюсь преобразовать XML-узлы в строку с помощью XSL. Любые мысли?
<ROOT>
<A>
<B>TESTING</B>
</A>
</ROOT>
XSL:
<xsl:variable name="nodestring" select="//A"/>
<xsl:value-of select="$nodestring"/>
Я пытаюсь преобразовать XML-узлы в строку с помощью XSL. Любые мысли?
Вам нужно сериализовать узлы. Самый простой для вашего примера будет что-то вроде
<xsl:template match="ROOT">
<xsl:variable name="nodestring">
<xsl:apply-templates select="//A" mode="serialize"/>
</xsl:variable>
<xsl:value-of select="$nodestring"/>
</xsl:template>
<xsl:template match="*" mode="serialize">
<xsl:text><</xsl:text>
<xsl:value-of select="name()"/>
<xsl:text>></xsl:text>
<xsl:apply-templates mode="serialize"/>
<xsl:text></</xsl:text>
<xsl:value-of select="name()"/>
<xsl:text>></xsl:text>
</xsl:template>
<xsl:template match="text()" mode="serialize">
<xsl:value-of select="."/>
</xsl:template>
Вышеупомянутые шаблоны сериализатора не обрабатываются, например. атрибуты, пространства имен или зарезервированные символы в текстовых узлах, но концепция должна быть ясной. Процесс XSLT работает в дереве node, и если вам нужно иметь доступ к "тегам", вам необходимо сериализовать узлы.
Основываясь на решении @jelovirt, вот более полный фрагмент кода:
<xsl:template match="*" mode="serialize">
<xsl:text><</xsl:text>
<xsl:value-of select="name()"/>
<xsl:apply-templates select="@*" mode="serialize" />
<xsl:choose>
<xsl:when test="node()">
<xsl:text>></xsl:text>
<xsl:apply-templates mode="serialize" />
<xsl:text></</xsl:text>
<xsl:value-of select="name()"/>
<xsl:text>></xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text> /></xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="@*" mode="serialize">
<xsl:text> </xsl:text>
<xsl:value-of select="name()"/>
<xsl:text>="</xsl:text>
<xsl:value-of select="."/>
<xsl:text>"</xsl:text>
</xsl:template>
<xsl:template match="text()" mode="serialize">
<xsl:value-of select="."/>
</xsl:template>
В версии XSLT 3.0. Смотрите эту ссылку W3 для fn: serialize. Это сработало для меня, используя SaxonPE.
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:output="http://www.w3.org/2010/xslt-xquery-serialization">
<xsl:variable name="output">
<output:serialization-parameters>
<output:method value="html"/>
</output:serialization-parameters>
</xsl:variable>
<xsl:template match="div">
<xsl:value-of select="serialize(., $output/output:serialization-parameters)" />
</xsl:template>
</xsl:stylesheet>
Саксон, необходимый для следующего решения. Я нахожу его здесь
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:saxon="http://saxon.sf.net/"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- To serialize with saxon:serialize() -->
<xsl:output name="default" indent="yes"
omit-xml-declaration="yes" />
<xsl:template match="*">
<xsl:variable name="node-set">
<xsl:element name="level1">
<xsl:element name="level2" />
<xsl:element name="level2" />
</xsl:element>
</xsl:variable>
<xsl:element name="input">
<xsl:copy-of select="$node-set" />
</xsl:element>
<xsl:element name="output">
<xsl:value-of select="saxon:serialize($node-set, 'default')" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
<xsl:template name="serializeNodeToString">
<xsl:param name="node"/>
<xsl:variable name="name" select="name($node)"/>
<xsl:if test="$name">
<xsl:value-of select="concat('<',$name)"/>
<xsl:for-each select="$node/@*">
<xsl:value-of select="concat(' ',name(),'="',.,'" ')"/>
</xsl:for-each>
<xsl:value-of select="concat('>',./text())"/>
</xsl:if>
<xsl:for-each select="$node/*">
<xsl:call-template name="serializeNodeToString">
<xsl:with-param name="node" select="."/>
</xsl:call-template>
</xsl:for-each>
<xsl:if test="$name">
<xsl:value-of select="concat('</',$name,'>')"/>
</xsl:if>
</xsl:template>
<xsl:template match="A">
<xsl:variable name="nodes" select="." />
<xsl:copy-of select="$nodes"/>
</xsl:template>
Обновлено на основе комментариев..
ОК. Я никогда не делал то, что вам нужно раньше, поэтому возьмите это с этой солью (я ее прикрываю). В основном вам нужно очень беспокоиться о двух вещах: персонажах, которым требуется экранирование и пробел. В этом случае строка, которую empo предоставила вам в комментариях выше, больше того, что вам нужно. Ниже приведен один из способов сделать ваш XSL-вывод следующим:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="A">
<input type="hidden" name="hiddenxml">
<xsl:attribute name="value">
<xsl:apply-templates select="." mode="id" />
</xsl:attribute>
</input>
</xsl:template>
<xsl:template match="*" mode="id" >
<xsl:text><</xsl:text><xsl:value-of select="name(.)" /><xsl:text>></xsl:text>
<xsl:apply-templates select="./*" mode="id" />
<xsl:value-of select="normalize-space(.)" />
<xsl:text></</xsl:text><xsl:value-of select="name(.)" /><xsl:text>></xsl:text>
</xsl:template>
</xsl:stylesheet>
Вам все равно нужно беспокоиться о других символах, которые требуют экранирования, например "", и я считаю, что вы можете использовать перевод или замену для этих
Мое решение:
<xsl:template name="serializeNodeToString">
<xsl:param name="node" />
<xsl:variable name="name" select="name($node)" />
<xsl:text><</xsl:text>
<xsl:value-of select="$name" />
<xsl:for-each select="$node/@*">
<xsl:text> </xsl:text>
<xsl:value-of select="name()" /><xsl:text>="</xsl:text>
<xsl:value-of select="." />
<xsl:text>"</xsl:text>
<xsl:text> </xsl:text>
</xsl:for-each>
<xsl:text>></xsl:text>
<xsl:value-of select="./text()" />
<xsl:for-each select="$node/*">
<xsl:call-template name="serializeNodeToString">
<xsl:with-param name="node" select="."/>
</xsl:call-template>
</xsl:for-each>
<xsl:text></</xsl:text>
<xsl:value-of select="$name" />
<xsl:text>></xsl:text>
</xsl:template>
Найдите "XML-принтер". Или просто взгляните на XSLT-код моего XPath Visualizer (хотя он создает представление XML для отображения в браузер, но вы получите эту идею).
С XSLT 1.0 вы можете использовать функцию XPath1.0 string()
в библиотеке основных функций, которая преобразует Node в строку:
<xsl:template match="A">
<xsl:variable name="nodeAsStr" select="string(.)" />
<xsl:copy-of select="$nodeAsStr"/><!-- or value-of -->
</xsl:template>
См. "Функция: строка string (object)" в разделе 4.3.
Это еще один вопрос, касающийся "XML pretty-printer" или "XML-дамп"... См. хорошие ответы здесь.
Мое решение для Saxon HE и имеет следующие преимущества:
Я успешно пробовал Saxon HE 9.5.X.
Речь идет о регистрации пользовательской функции расширения с этим содержимым:
import java.io.StringWriter;
import net.sf.saxon.expr.XPathContext;
import net.sf.saxon.lib.ExtensionFunctionCall;
import net.sf.saxon.lib.ExtensionFunctionDefinition;
import net.sf.saxon.om.Sequence;
import net.sf.saxon.om.StructuredQName;
import net.sf.saxon.s9api.Processor;
import net.sf.saxon.s9api.SaxonApiException;
import net.sf.saxon.s9api.Serializer;
import net.sf.saxon.s9api.XdmValue;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.value.SequenceType;
import net.sf.saxon.value.StringValue;
@SuppressWarnings("serial")
public class XmlSerializer extends ExtensionFunctionDefinition {
@Override
public StructuredQName getFunctionQName() {
return new StructuredQName("vis", "my.custom.uri", "serialize-xml");
}
@Override
public SequenceType[] getArgumentTypes() {
return new SequenceType[] { SequenceType.SINGLE_NODE };
}
@Override
public SequenceType getResultType(SequenceType[] sequenceTypes) {
return SequenceType.SINGLE_STRING;
}
@Override
public ExtensionFunctionCall makeCallExpression() {
return new ExtensionFunctionCall() {
@Override
public Sequence call(XPathContext ctx, Sequence[] secs) throws XPathException {
StringWriter escr = new StringWriter();
try {
if (secs.length == 0) {
throw new XPathException("Missing argument");
} else {
Serializer serializer = new Processor(ctx.getConfiguration()).newSerializer(escr);
serializador.setOutputProperty(Serializer.Property.OMIT_XML_DECLARATION, "yes");
serializer.serializeXdmValue(XdmValue.wrap(secs[0]));
}
return new StringValue(escr.toString());
} catch (SaxonApiException ex) {
throw new XPathException("Error when invoking serialize-xml()", ex);
}
}
};
}
}
Вы можете использовать эту функцию следующим образом:
<xs:value-of xmlns:vis="my.custom.uri" select="vis:serialize-xml(someNode)"/>
Обратный процесс описан здесь.
Все решения пропускают текст после узла и атрибуты в одинарных кавычках. пример
<b f1='"' f2="'">one</b>
,
<b>two</b>
Мое решение на основе @Илья-Харламов
<xsl:template name="f_serialize_node_to_string" xmlns:exsl="http://exslt.org/common" extension-element-prefixes="exsl">
<xsl:param name="node"/>
<xsl:variable name="node_" select="exsl:node-set($node)"/>
<xsl:variable name="name" select="name($node_)"/>
<xsl:variable name="q">'</xsl:variable>
<xsl:variable name="qq">"</xsl:variable>
<xsl:if test="$name">
<xsl:value-of select="concat('<',$name)"/>
<xsl:for-each select="$node_/@*">
<xsl:choose>
<xsl:when test="contains(., $qq)">
<xsl:value-of select="concat(' ',name(),'=',$q,.,$q,' ')"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(' ',name(),'="',.,'" ')"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
<xsl:value-of select="concat('>', ./text())"/>
</xsl:if>
<xsl:for-each select="$node_/*">
<xsl:call-template name="f_serialize_node_to_string">
<xsl:with-param name="node" select="."/>
</xsl:call-template>
</xsl:for-each>
<xsl:if test="$name">
<xsl:value-of select="concat('</',$name,'>')"/>
</xsl:if>
<xsl:if test="$node_/following-sibling::text()">
<xsl:value-of select="$node_/following-sibling::text()" />
</xsl:if>
</xsl:template>