XSLT 1.0遍历XML中' RH'开头文本块无输出,求解决方案
XML文本块提取问题及解决建议
问题描述
输入XML结构如下(root节点包含一段多行文本,内含两个以 RH开头的完整块,每个块以EF开头的行结束):
<root> RH03051CDSIA280524CM1490301951171 610000001 93001 G0305101700000000004575EUR270524C000000000000,00IT44 620000001001270524270524D000000000649,3450TE ITDA00DPN145 630000001001HAYS SRL/AVIS BUDGET ITALIA S.P.A./AR885265/2355070853/B2B/RCUR/OE5OA5200P4907R3 640000001EUR270524C000000000000,00 EF03051CDSIA280524CM1490301951171 0000001 0000006 RH03051CDSIA280524CM1490301951349 610000001 93001 Z0305101699000078389249USD270524C000000001320,97IT72 640000001USD270524C000000001320,97 EF03051CDSIA280524CM1490301951349 0000001 0000004 </root>
使用以下XSLT 1.0代码尝试提取块时无输出:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="http://exslt.org/common" xmlns:ns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.03" xmlns:BODY="urn:CBI:xsd:CBIBdyBkToCstmrStmtReq.00.01.02" xmlns:LMSG="urn:CBI:xsd:CBIBkToCstmrStmtReqLogMsg.00.01.02" xmlns:HE2E="urn:CBI:xsd:CBIHdrSrv.001.07" xmlns:HTRT="urn:CBI:xsd:CBIHdrTrt.001.07" xmlns:IDST="urn:CBI:xsd:CBIIdyStmtReqLogMsg.00.01.02" xmlns:DLST="urn:CBI:xsd:CBIDlyStmtReqLogMsg.00.01.02" xmlns:PRST="urn:CBI:xsd:CBIPrdcStmtReqLogMsg.00.01.02" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:str="http://exslt.org/strings" xmlns:myns="py_ns" extension-element-prefixes="exsl myns" exclude-result-prefixes="exsl ns"> <xsl:output encoding="UTF-8" standalone="yes" indent="yes" method="xml" /> <xsl:template match="/"> <xsl:for-each select="//text[starts-with(., ' RH')]"> <xsl:value-of select="." /> </xsl:for-each> </xsl:template> </xsl:stylesheet>
需求:用lxml库(已引入EXSLT和str扩展)提取每个 RH开头的完整文本块(从RH行到对应EF行结束)。
问题原因
- XPath选择器错误:
//text是查找名为<text>的元素,而非XML中的文本节点。正确的文本节点选择器是text(),原代码因此无法匹配到任何内容。 - 未收集完整块:即使找到开头的RH文本节点,原代码仅输出该节点内容,未收集后续直到EF结束的所有关联文本节点。
解决建议
修正后的XSLT代码
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="http://exslt.org/common" xmlns:str="http://exslt.org/strings" xmlns:myns="py_ns" extension-element-prefixes="exsl myns" exclude-result-prefixes="exsl"> <xsl:output encoding="UTF-8" standalone="yes" indent="yes" method="xml" /> <xsl:template match="/"> <blocks> <!-- 匹配所有以" RH"开头的文本节点 --> <xsl:for-each select="/root/text()[starts-with(., ' RH')]"> <block> <!-- 输出当前RH行 --> <xsl:value-of select="." /> <!-- 收集后续所有文本节点,直到下一个RH开头或文档结束 --> <xsl:for-each select="following-sibling::text()[ not(starts-with(., ' RH')) and generate-id(preceding-sibling::text()[starts-with(., ' RH')][1]) = generate-id(current()) ]"> <xsl:value-of select="." /> </xsl:for-each> </block> </xsl:for-each> </blocks> </xsl:template> </xsl:stylesheet>
关键修改说明
- 修正文本节点选择:使用
/root/text()[starts-with(., ' RH')]精确匹配root下以RH开头的文本节点。 - 完整块收集:通过
following-sibling::text()遍历后续文本节点,用generate-id确保只收集当前RH块对应的后续内容(直到下一个RH出现前的所有节点)。 - 输出结构化结果:用
<blocks>和<block>标签包裹提取的内容,方便后续处理。
额外优化(可选)
如果需要将块内的多行文本合并为单行(去除多余换行和空格),可以使用EXSLT的str:replace和normalize-space:
<!-- 在block内替换换行和多余空格 --> <xsl:variable name="raw-block"> <xsl:value-of select="." /> <xsl:for-each select="following-sibling::text()[ not(starts-with(., ' RH')) and generate-id(preceding-sibling::text()[starts-with(., ' RH')][1]) = generate-id(current()) ]"> <xsl:value-of select="." /> </xsl:for-each> </xsl:variable> <xsl:value-of select="normalize-space(str:replace($raw-block, '\s+', ' '))" />
内容的提问来源于stack exchange,提问作者Catanzaro
相关产品推荐
相关产品推荐

