Can this preg_replace_callback method be done in xPath or XQuery?

家住魔仙堡 提交于 2019-12-13 06:55:19

问题


The script below takes the $myContent block and does the doReplace() function on the $myKeyword value. The problem I have is that it does not know if the replacement text is already inside a tag or not.

I need to modify the doReplace() function so that it does not touch any text inside of or as attributes of the named tags (h1, h2, h3, i, u, b, strong, em, img).

I think it would be better to convert this over to an xPath method and looking for suggestions on how it might be done with xPath. So the question is: "How would you convert this to xPath"?

Note: The count variable in the replace function is there because I'm only replacing on the first three appearances of the keyword. First time it appears it gets bolded, next time it gets italicized and the third time it appears, it gets underlined.

$myKeyword = "test keyword";

$myContent = "My content contains the "test keyword". 
Don't do the replace if the test keyword is inside:
h1, h2, h3, i, u, b, strong, em, tags. 
<h1>This test keyword would not be replaced</h1>";

$myContent = preg_replace_callback("/\b($mykeyword)\b/i","doReplace", $myContent);

function doReplace($matches)
{
    static $count = 0;
    switch($count++) {
    case 0: return ' <b>'.trim($matches[1]).'</b>';
    case 1: return ' <em>'.trim($matches[1]).'</em>';
    case 2: return ' <u>'.trim($matches[1]).'</u>';
    default: return $matches[1];
    }
}

回答1:


You can't in XPath 1.0 nor 2.0, because you need recursion to express this algorithm. You could use an extension function, of course.

This XQuery:

declare variable $match as xs:string external;
declare variable $replace as xs:string external;
declare variable $preserve as xs:string external;
declare variable $vPreserve := tokenize($preserve,',');
declare function local:copy-replace($element as element()) {   
   element {node-name($element)}
           {$element/@*,  
            for $child in $element/node()   
                return if ($child instance of element())   
                       then local:copy-replace($child)   
                       else if ($child instance of text() and
                                not(name($element)=$vPreserve))
                            then replace($child,$match,$replace)
                            else $child
           }
};   
local:copy-replace(/*) 

With this input:

<html>
    <h1>This test keyword would not be replaced</h1>
    <p>This test keyword should be replaced</p>
</html>

Output:

<html>
    <h1>This test keyword would not be replaced</h1>
    <p>This replaced should be replaced</p>
</html>

Also this stylesheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml"/>
    <xsl:param name="pMatch" select="'test keyword'"/>
    <xsl:param name="pReplace" select="'replaced'"/>
    <xsl:param name="pPreserve" select="'h1,h2,h3,i,u,b,strong,em'"/>
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="text()">
        <xsl:choose>
            <xsl:when test="contains(concat(',',$pPreserve,','),
                                     concat(',',name(..),','))">
                <xsl:value-of select="."/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:call-template name="replace"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    <xsl:template name="replace">
        <xsl:param name="pString" select="."/>
        <xsl:choose>
            <xsl:when test="contains($pString,$pMatch)">
                <xsl:value-of
                     select="concat(substring-before($pString,
                                                     $pMatch),
                                    $pReplace)"/>
                <xsl:call-template name="replace">
                    <xsl:with-param name="pString"
                                    select="substring-after($pString,
                                                            $pMatch)"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="$pString"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

EDIT: Better XQuery.



来源:https://stackoverflow.com/questions/4077624/can-this-preg-replace-callback-method-be-done-in-xpath-or-xquery

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!