简体   繁体   English

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

[英]XSLT: How to convert XML Node to String

<ROOT>
   <A>
      <B>TESTING</B>
   </A>
</ROOT>

XSL: XSL:

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

I am trying to convert XML nodeset to string using XSL.我正在尝试使用 XSL 将 XML 节点集转换为字符串。 Any thoughts?有什么想法吗?

You need to serialize the nodes.您需要序列化节点。 The most simple for your example would be something like您的示例最简单的方法是

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

The above serializer templates do not handle eg attributes, namespaces, or reserved characters in text nodes, but the concept should be clear.上面的序列化程序模板不处理文本节点中的属性、名称空间或保留字符等,但概念应该很清楚。 XSLT process works on a node tree and if you need to have access to "tags", you need to serialize the nodes. XSLT 进程在节点树上工作,如果您需要访问“标签”,则需要序列化节点。

Based on @jelovirt solution, here is a more complete piece of code:基于@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>

In XSLT Version 3.0.在 XSLT 版本 3.0 中。 See this W3 link for fn:serialize .请参阅此 W3 链接以获取 fn:serialize This worked for me using SaxonPE.这对我使用 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>
<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>

Saxon required for following solution.以下解决方案需要撒克逊人。 I find it 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>

My solution:我的解决方案:

<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>
<xsl:template match="A">
  <xsl:variable name="nodes" select="." />
  <xsl:copy-of select="$nodes"/>
</xsl:template>

Updated based on comments..根据评论更新..

OK I've never done exactly what you need before, so take this with that grain of salt (I'm winging it).好的,我以前从来没有完全做过你需要的东西,所以把它和那粒盐一起吃(我正在努力)。 Basically you need to be very concerned about 2 things: characters that require escaping and white space.基本上你需要非常关心两件事:需要 escaping 和空格的字符。 In this case, the string that empo gave you in the comments above is more what you're after.在这种情况下, empo在上面的评论中给你的字符串是你所追求的。 Below is one way that you can make your XSL output that:下面是一种使您的 XSL output 的方法:

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

You still need to be concerned with other characters that require escaping like " ' & I believe you can use translate or replace for those您仍然需要关注其他需要 escaping 的字符,例如“' & 我相信您可以使用翻译或替换那些

About "convert Node to String"关于“将节点转换为字符串”

With XSLT 1.0, you can use the XPath1.0 string() function of the Core Function Library, that converts a node to a string:使用 XSLT 1.0,您可以使用核心 Function 库的 XPath1.0 string() function,它将节点转换为字符串:

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

See "Function: string string(object)" at section 4.3 .请参阅第 4.3 节的“函数:字符串字符串(对象)”。

About "convert Node to XML pretty-printer"关于“将节点转换为 XML 漂亮打印机”

It this another question, about "XML pretty-printer" or "XML dump"... See good answers here.这是另一个关于“XML 漂亮打印机”或“XML 转储”的问题......在这里查看好的答案。

My solution is for Saxon HE, and have this advantages:我的解决方案是针对 Saxon HE,并且具有以下优势:

  • it doesn't require licensing它不需要许可
  • supports namespaces, CDATA, escaping of special characters and many advanced XML features.支持命名空间、CDATA、特殊字符的 escaping 和许多高级 XML 功能。

I've tried successfully with Saxon HE 9.5.X.我已成功尝试使用 Saxon HE 9.5.X。

It is about registering a custom extension function with these contents:这是关于使用以下内容注册自定义扩展 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);
                }
            }
        };
    }
}

You can use this function as follows:您可以按如下方式使用此 function:

<xs:value-of xmlns:vis="my.custom.uri" select="vis:serialize-xml(someNode)"/>

The inverse process is documented here . 此处记录了逆过程。

Search for "XML pretty-printer".搜索“XML 漂亮打印机”。 Or just have a look at the XSLT code of my XPath Visualizer (though it produces XML representation to be displayed in a browser, but you'll get the idea).或者看看我的XPath Visualizer的 XSLT 代码(尽管它会生成 XML 表示以显示在浏览器中,但你会明白的)。

All solutions miss text after node and attributes in single quotes.所有解决方案都缺少单引号中的节点和属性之后的文本。 Example例子

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

My solution based on @Ilya-Kharlamov我的解决方案基于@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>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM