XSLT:读取作为字符串传递的XML文档参数

3
我遇到了与另一位工程师的问题。作为一名新工程师,我必须找出解决方案,但我还没有能够做到。任何帮助都将不胜感激,最接近的东西是使用XSLT对字符串进行分块,但还不太行。我只能使用XSL v1.0。
一个巨大的字符串作为参数传递给我的样式表。它最初来自一个XML文档。这个字符串看起来像这样。
其中<xsl:value-of select="$servers"/>,其中$servers是传递给我的字符串的参数。这个字符串看起来像这样:
<license><active_servers><server><name>MIKE</name><capacity>18</capacity><status>0</status><expiration></expiration><left>0</left><comment></comment></server><server><name>Susie</name><capacity>0</capacity><status>1</status><expiration>2014-07-04T00:00:00Z</expiration><left>5238568</left><comment></comment></server><server><name>Zoe</name><capacity>5000</capacity><status>1</status><expiration></expiration><left>0</left><comment></comment></server></active_servers></license>
这是传递给样式表的参数的xml数据的一部分。实际文档有300多行数据。唯一能区分这些“节点”的独特事物是<server></server>。如果这是一个非常大的字符串,有没有办法从中获取数据?
例如,我需要找到“Zoe”,并查看她是否有“过期时间”,如果没有,我需要她的“状态”。因此,Zoe的状态将显示为“1”,因为她没有到期时间。而MIKE将显示“0”,Susie将显示2014-07-04T00:00:00Z。
我在谷歌和堆栈溢出上寻找解析/读取巨大字符串的解决方案,但我还没有找到足够接近的解决方案可以使其工作。目前,我被困在没有可用副本的境地,并且进行了2天的研究,没有取得任何进展。

一个字符串不是XML,不能被解析为XML。你不能通过传递路径作为参数来传递一个实际的XML文档吗? - michael.hor257k
我在4天前给另一位在世界另一端的工程师发了电子邮件,但是没有收到回复。所以我认为他想要这样。我想也许我错过了能够读取这样一个字符串的东西。我知道我可以测试某个关键词的字符串,比如MIKE或Zoe,但我不认为我可以测试关键词后面的数据,我对吗? - misterbear
您可以使用XSLT 1.0提供的字符串函数将字符串解析为字符串,但这将非常繁琐且容易出错。您应该真正努力将输入处理为XML - 这意味着要么(1)访问XML文件中的原始数据; 要么(2)进行两个连续的转换,第一个转换将参数保存到具有已知路径的文件中,第二个转换从该文件中读取。 - michael.hor257k
你的第一个解决方案是将参数作为带有原始数据的XML文档发送,这正是我所想到的解决方案。我会再和工程师谈谈,看看能否改变他的想法。感谢你的帮助和确认我走在正确的道路上!你能发布一个答案吗?我会标记它为已回答并关闭。 - misterbear
2个回答

3
无法将字符串解析为XML,正如@michael.hor257k在接受的答案中指出的那样,但是有一种方法可以通过将其作为嵌入式文档加载来将您的字符串视为节点集。这可以使用数据URI方案document()函数来实现。然而,XSLT 1.0规范警告说,这是与实现相关的(处理器不需要支持任何URI方案)。我使用Xalan和Saxon 6等XSLT 1.0处理器进行了测试,并且它能够运行。
解决方案是将您的字符串附加到数据URI方案data:text/xml后面并用逗号分隔。然后,您可以将此字符串传递给document()函数,它将解析它作为XML文件。
document(concat('data:text/xml,',$servers))

您可以将任何模板应用于节点集。
以下是一个样式表,其中包含一个$servers参数,该参数应接收包含XML数据的字符串。它将解析该字符串,转换模板中的节点,并生成带有一些数据的XML输出:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output indent="yes"/>
    <xsl:param name="servers"/>

    <xsl:template match="/">
        <xsl:apply-templates select="document(concat('data:text/xml,',$servers))/license"/>
    </xsl:template>
    
    <xsl:template match="license">
        <results>
            <xsl:apply-templates/>
        </results>
    </xsl:template>
    
    <!-- server without expiration - get status -->
    <xsl:template match="server[not(string(expiration))]">
        <server name="{name}" status="{status}" />
    </xsl:template>
    
    <!-- server with expiration - get expiration -->
    <xsl:template match="server">
        <server name="{name}" expiration="{expiration}" />
    </xsl:template>
    
</xsl:stylesheet>

如果您以任何源代码运行此程序,并将数据作为参数传递,则会得到以下结果:
<results>
   <server name="MIKE" status="0"/>
   <server name="Susie" expiration="2014-07-04T00:00:00Z"/>
   <server name="Zoe" status="1"/>
</results>

更新: 这个功能还取决于解析器对"data-uris"的支持。由于我的XML环境已经有这方面的支持,所以更改不同的XSLT处理器没有任何区别。我使用的是Mac OS X环境下的Oxygen XML Editor 15.2。当我确定它使用的确切解析器时,我会更新此信息。


这是一个有趣的想法,但我无法在任何处理器上重现您的成功。我使用Saxon和Xalan都会收到“URL格式错误”的警告。您能否在此处更正示例:http://xsltransform.net/bFukv8r - michael.hor257k
@michael.hor257k 看起来这取决于解析器对数据URI的支持。它在Oxygen XML Editor 15.2中可以工作(以及命令行环境,它共享相同的配置),但我还没有能够让它在标准Java 8 TraX实现中工作。 - helderdarocha

3
一个字符串不是XML格式的,不能作为XML进行解析。当然,你可以使用XSLT 1.0提供的字符串函数将其解析为字符串,但这样做可能会很繁琐且容易出错。
如果可能的话,将实际的XML文档路径作为参数传递。或者,调用两个转换来完成,第一个转换将参数保存到已知路径的文件中,第二个转换从该文件中读取数据。
另请参阅:
https://dev59.com/jm3Xa4cB1Zd3GeqPg6Et#14512924

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接