XSLT: Как преобразовать XML-узел в строку - PullRequest
10 голосов
/ 14 июля 2011
<ROOT>
   <A>
      <B>TESTING</B>
   </A>
</ROOT>

XSL:

<xsl:variable name="nodestring" select="//A"/>
<xsl:value-of select="$nodestring"/>

Я пытаюсь преобразовать набор узлов XML в строку, используя XSL. Есть мысли?

Ответы [ 11 ]

12 голосов
/ 14 июля 2011

Вам необходимо сериализовать узлы. Самым простым для вашего примера будет что-то вроде

<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>&lt;</xsl:text>
  <xsl:value-of select="name()"/>
  <xsl:text>&gt;</xsl:text>
  <xsl:apply-templates mode="serialize"/>
  <xsl:text>&lt;/</xsl:text>
  <xsl:value-of select="name()"/>
  <xsl:text>&gt;</xsl:text>
</xsl:template>

<xsl:template match="text()" mode="serialize">
  <xsl:value-of select="."/>
</xsl:template>

Указанные выше шаблоны сериализатора не обрабатываются, например, атрибуты, пространства имен или зарезервированные символы в текстовых узлах, но концепция должна быть ясной. Процесс XSLT работает на дереве узлов, и если вам нужен доступ к «тегам», вам нужно сериализовать узлы.

8 голосов
/ 03 апреля 2013

На основе решения @jelovirt приведем более полный код:

<xsl:template match="*" mode="serialize">
    <xsl:text>&lt;</xsl:text>
    <xsl:value-of select="name()"/>
    <xsl:apply-templates select="@*" mode="serialize" />
    <xsl:choose>
        <xsl:when test="node()">
            <xsl:text>&gt;</xsl:text>
            <xsl:apply-templates mode="serialize" />
            <xsl:text>&lt;/</xsl:text>
            <xsl:value-of select="name()"/>
            <xsl:text>&gt;</xsl:text>
        </xsl:when>
        <xsl:otherwise>
            <xsl:text> /&gt;</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>
4 голосов
/ 22 марта 2014

В XSLT версии 3.0. См. http://myxsl.net/xslcompiledtransform/extensions/w3c.xpath.xsl#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>
3 голосов
/ 14 декабря 2016
<xsl:template name="serializeNodeToString">
    <xsl:param name="node"/>
    <xsl:variable name="name" select="name($node)"/>
    <xsl:if test="$name">
        <xsl:value-of select="concat('&lt;',$name)"/>
        <xsl:for-each select="$node/@*">
            <xsl:value-of select="concat(' ',name(),'=&quot;',.,'&quot; ')"/>
        </xsl:for-each>
        <xsl:value-of select="concat('&gt;',./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('&lt;/',$name,'&gt;')"/>
    </xsl:if>
</xsl:template>
3 голосов
/ 13 августа 2011

Саксонский требуется для следующего решения. Я нахожу это здесь

<?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>
2 голосов
/ 02 декабря 2015

Мое решение:

<xsl:template name="serializeNodeToString">
    <xsl:param name="node" />
    <xsl:variable name="name" select="name($node)" />

    <xsl:text>&lt;</xsl:text>
    <xsl:value-of select="$name" />
    <xsl:for-each select="$node/@*">
        <xsl:text> </xsl:text>
        <xsl:value-of select="name()" /><xsl:text>=&quot;</xsl:text>
            <xsl:value-of select="." /> 
        <xsl:text>&quot;</xsl:text>
        <xsl:text> </xsl:text>
    </xsl:for-each>
    <xsl:text>&gt;</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>&lt;/</xsl:text>
    <xsl:value-of select="$name" />
    <xsl:text>&gt;</xsl:text>
</xsl:template>
2 голосов
/ 14 июля 2011
<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>&lt;</xsl:text><xsl:value-of select="name(.)" /><xsl:text>&gt;</xsl:text>
  <xsl:apply-templates select="./*" mode="id" />
  <xsl:value-of select="normalize-space(.)" />
  <xsl:text>&lt;/</xsl:text><xsl:value-of select="name(.)" /><xsl:text>&gt;</xsl:text>
</xsl:template>

</xsl:stylesheet>

Вам все еще нужно позаботиться о других символах, которые требуют экранирования, например "", и я считаю, что вы можете использовать перевод или замену для этих

1 голос
/ 01 июля 2014

Мое решение предназначено для Saxon HE и имеет следующие преимущества:

  • оно не требует лицензирования
  • поддерживает пространства имен, CDATA, экранирование специальных символов и многие расширенные функции XML.

Я успешно пытался с 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)"/>

Обратный процесс задокументирован здесь .

1 голос
/ 15 апреля 2013

О преобразовании узла в строку

В XSLT 1.0 вы можете использовать функцию XPath1.0 string() библиотеки базовых функций, которая преобразует узел в строку:

<xsl:template match="A">
  <xsl:variable name="nodeAsStr" select="string(.)" />
  <xsl:copy-of select="$nodeAsStr"/><!-- or value-of -->
</xsl:template>

См. «Функция: строковая строка (объект)» в разделе 4.3 .

Об "преобразовании узла в XML pretty-printer"

Это еще один вопрос о "XML pretty-printer" или "XML dump" ... Смотрите хорошие ответы здесь.

1 голос
/ 15 июля 2011

Поиск "XML pretty-printer". Или просто взгляните на XSLT-код моего XPath Visualizer (хотя он создает представление XML для отображения в браузере, но вы поймете, что идея).

...