使用XSLT将字符串替换形式的密文应用于HTML文档



我有大量的HTML(可能还有其他xml(文档需要编辑。

编辑通常采用"John Doe"->"[人员A]"的形式。要编辑的文本可能在标题或段落中,但几乎总是在段落中。

真的很简单的字符串替换。不是很复杂的事情。

然而,我确实希望保留文档结构,并且我宁愿不重新发明任何轮子。文档文本中的字符串替换可能完成任务,但也可能破坏文档结构,因此这将是最后一个选项。

现在,我已经盯着XSLT看了一个小时,并试图强制"str:replace"执行我的命令。我不会让你看到我失败的微弱尝试,但我会问:有没有一种简单明了的方法可以使用XSLT应用我的密文,你能把它发布在这里吗?

提前谢谢。

更新:应Martin Honnen的请求,我将添加我的输入文件以及用于获取最新错误消息的命令。从中可以明显看出,当涉及到XSLT:-(时,我是一个完全的n00b

.html文件:

<!DOCTYPE HTML PUBLIC"//W3C//DTD HTML 4.0过渡//EN"><html><头部><meta-http-equiv="content-type"content="text.html;charset=utf-8"/><标题>今天日期<标题><meta name="created"content="2020-11-04T30:45:00"/><头部><身体><ol start="2"><李>lt;p>约翰·多伊9号。fux 2057与亨利Fluebottom成立了Doe&;Fluebottom小工具股份有限公司<p><ol><身体><html>

XSLT转换文件:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:template match="p">
<xsl:copy>
<xsl:attribute name="matchesPattern">
<xsl:copy-of select='str:replace("John Doe", ".*",  "[Person A]")'/>
</xsl:attribute>
<xsl:copy-of select='str:replace("Henry Fluebottom", ".*",  "[Person B]")'/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

命令和输出:

$  xsltproc -html transform.xsl example.html
xmlXPathCompOpEval: function replace bound to undefined prefix str
xmlXPathCompiledEval: 2 objects left on the stack.
<?xml version="1.0"?>

TodaysDate


<p matchesPattern=""/>  
$ 

xsltproc基于libxslt,支持各种EXSLT函数,如str:replace,要使用它,您需要声明名称空间

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:str="http://exslt.org/strings"
exclude-result-prefixes="str"
version="1.0">
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="p//text()">
<xsl:value-of select="str:replace(., 'John Doe', '[Person A]')"/>
</xsl:template>
</xsl:stylesheet>

XSLT1.0中没有简单的方法可以对同一字符串执行多个替换。您需要使用递归命名模板,一次执行一个替换操作,然后移动到当前查找字符串的下一个实例,或者(如果不存在下一个示例(移动到下一个查找/替换对。

考虑以下示例:

输入

<html>
<head>
<title>John Doe and Henry Fluebottom</title>
</head>
<body>
<p>John Doe is a person. John Doe on 9. fux 2057 together with Henry Fluebottom formed the company Doe &amp; Fluebottom Widgets Inc. Henry Fluebottom is also a person.</p>
</body>
</html>

XSLT 1.0(+EXSLT node-set((函数(

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" omit-xml-declaration="yes" version="1.0" encoding="utf-8" indent="yes"/>
<!-- identity transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:variable name="dictionary">
<entry find="John Doe" replace="[Person A]"/>
<entry find="Henry Fluebottom" replace="[Person B]"/>
</xsl:variable>
<xsl:template match="text()">
<xsl:call-template name="multi-replace">
<xsl:with-param name="string" select="normalize-space(.)"/>
<xsl:with-param name="entries" select="exsl:node-set($dictionary)/entry"/>"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="multi-replace">
<xsl:param name="string"/>
<xsl:param name="entries"/>
<xsl:choose>
<xsl:when test="$entries">
<xsl:call-template name="multi-replace">
<xsl:with-param name="string">
<xsl:call-template name="replace">
<xsl:with-param name="string" select="$string"/>
<xsl:with-param name="search-string" select="$entries[1]/@find"/>
<xsl:with-param name="replace-string" select="$entries[1]/@replace"/>
</xsl:call-template>
</xsl:with-param>
<xsl:with-param name="entries" select="$entries[position() > 1]"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$string"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="replace">
<xsl:param name="string"/>
<xsl:param name="search-string"/>
<xsl:param name="replace-string"/>
<xsl:choose>
<xsl:when test="contains($string, $search-string)">
<xsl:value-of select="substring-before($string, $search-string)"/>
<xsl:value-of select="$replace-string"/>
<xsl:call-template name="replace">
<xsl:with-param name="string" select="substring-after($string, $search-string)"/>
<xsl:with-param name="search-string" select="$search-string"/>
<xsl:with-param name="replace-string" select="$replace-string"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$string"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>

结果

<html>
<head>
<title>[Person A] and [Person B]</title>
</head>
<body>
<p>[Person A] is a person. [Person A] on 9. fux 2057 together with [Person B] formed the company Doe &amp; Fluebottom Widgets Inc. [Person B] is also a person.</p>
</body>
</html>

正如您所看到的,这将替换输入文档中任何位置的搜索字符串的所有实例(属性除外(,同时保留文档的结构。


请注意,示例中的输入实际上并不包含"Henry Fluebottom"搜索字符串。你可能想通过调用第一个模板来绕过这个问题:

<xsl:with-param name="string" select="normalize-space(.)"/>

而不是:

<xsl:with-param name="string" select="."/>

第一个问题是找到一个真正支持字符串替换的XSLT处理器。replace((函数是XSLT2.0+中的标准函数,但在XSLT1.0中不存在。一些XSLT1.0处理器在不同的名称空间中支持扩展函数str:replace((,但至少需要将名称空间声明xmlns:str="http://exslt.org/strings"添加到样式表中才能定位该函数。我不知道这是否可行(我不知道是否有任何方法可以将此函数与xsltproc一起使用(;我的建议是使用XSLT2.0+处理器。

下一个问题是调用函数的方式。通常,正确的调用是

replace(., "John Doe", "[Person A]")

不过,要在同一根绳子上进行多次替换,你还需要再跳过几个环节。

我不知道你想用<xsl:attribute name="matchesPattern">指令实现什么。

最新更新