XSLT:如何将 XML 节点转换为字符串

Posted

技术标签:

【中文标题】XSLT:如何将 XML 节点转换为字符串【英文标题】:XSLT: How to convert XML Node to String 【发布时间】:2011-10-05 11:59:16 【问题描述】:
<ROOT>
   <A>
      <B>TESTING</B>
   </A>
</ROOT>

XSL:

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

我正在尝试使用 XSL 将 XML 节点集转换为字符串。有什么想法吗?

【问题讨论】:

您希望输出为:TESTING ? mikey - 我需要的输出完全相同。 得到它的工作并在下面发布。 在这种情况下,您想要的输出将是 &amp;lt;A&amp;gt;&amp;lt;B&amp;lt;TESTING&amp;gt;/B&amp;gt;&amp;lt;/A&amp;gt; 我想。否则你可以简单地使用xsl:copy-of 【参考方案1】:

在 XSLT 3.0 版中。见this W3 link for 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>

【讨论】:

【参考方案2】:

所有解决方案都缺少单引号中的节点和属性之后的文本。 示例

<b f1='"' f2="'">one</b>
,
<b>two</b>

我的解决方案基于 @Ilya-Kharlamov

<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('&lt;',$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(),'=&quot;',.,'&quot; ')"/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:for-each>
        <xsl:value-of select="concat('&gt;', ./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('&lt;/',$name,'&gt;')"/>
    </xsl:if>
    <xsl:if test="$node_/following-sibling::text()">
        <xsl:value-of select="$node_/following-sibling::text()" />
    </xsl:if>
</xsl:template>

【讨论】:

为了向您展示此任务可能变得多么复杂,请检查您的解决方案如何错误地处理此格式良好的 XML &lt;test a='1"2'/&gt;【参考方案3】:
<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>

【讨论】:

在消息中调用此模板的示例,用于调试目的:&lt;xsl:message&gt;&lt;xsl:call-template name="serializeNodeToString"&gt; &lt;xsl:with-param name="node" select = "."/&gt; &lt;/xsl:call-template&gt;&lt;/xsl:message&gt;【参考方案4】:

我的解决方案:

<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>

【讨论】:

【参考方案5】:

我的解决方案是针对 Saxon HE,并且具有以下优势:

不需要许可 支持命名空间、CDATA、特殊字符转义和许多高级 XML 功能。

我已成功尝试使用 Saxon HE 9.5.X。

关于registering a custom extension function的内容如下:

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)"/>

逆过程is documented here。

【讨论】:

【参考方案6】:

关于“将节点转换为字符串”

使用 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>

参见“函数:字符串字符串(对象)”at section 4.3。

关于“将 Node 转换为 XML 漂亮打印机”

这是另一个关于“XML 漂亮打印机”或“XML 转储”的问题......在这里查看好的答案。

【讨论】:

【参考方案7】:

基于@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>

【讨论】:

【参考方案8】:

以下解决方案需要撒克逊人。我找到了here

<?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>

【讨论】:

此功能需要付费版的 Saxon。 该功能在 Saxon-B 中也可用【参考方案9】:

搜索“XML 漂亮打印机”。或者看看我的 XPath Visualizer 的 XSLT 代码(虽然它会生成要在浏览器中显示的 XML 表示,但你会明白的)。

【讨论】:

【参考方案10】:
<xsl:template match="A">
  <xsl:variable name="nodes" select="." />
  <xsl:copy-of select="$nodes"/>
</xsl:template>

根据 cmets 更新..

好的,我以前从来没有完全按照您的要求完成过,所以把它和那粒盐一起吃吧(我正在努力)。基本上你需要非常关心两件事:需要转义的字符和空格。在这种情况下,empo 在上面的 cmets 中给你的字符串更多的是你所追求的。下面是一种可以让您的 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>

您仍然需要关注其他需要转义的字符,例如 " ' & 我相信您可以对这些字符使用翻译或替换

【讨论】:

谢谢麦克。我想将 的输出存储到变量中。一旦我存储它,它就会丢失标签信息。 我们将 nodes 放入变量中。要输出标签信息,您可以使用 copy-of 来完成。它不会丢失,只是取决于您如何显示它。 value-of 只会显示标签值而不是标签本身。 输入>。如果我这样做,只会出现值,而不是整个 xml 字符串 @Kalyan,没错。您不能将节点作为值复制到属性中。您需要正确转义标签。【参考方案11】:

您需要序列化节点。你的例子最简单的就是

<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 进程在节点树上工作,如果您需要访问“标签”,则需要对节点进行序列化。

【讨论】:

@Kaylan,然后您将按如下方式填充属性:&lt;input type="hidden" name="hiddenxml"&gt; &lt;xsl:attribute name="value"&gt;&lt;xsl:value-of select="$nodestring"/&gt;&lt;/xsl:attribute&gt;&lt;/input&gt;

以上是关于XSLT:如何将 XML 节点转换为字符串的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 XSLT 替换 XML 节点名称中的字符 - 更改根元素

XSLT - 如何将节点内的内联/转义 XML 视为嵌套节点

XSLT 中的十进制格式以及字符

使用 Python 或 XSLT 将复杂的 XML 转换为 CSV

使用 xslt 将 xml 复杂节点元素拆分为多个节点

使用 XSLT 转换 xml 中的特殊字符