XSLT 1.0 搜索替换给定字符串中的缩写

XSLT 1.0 Search Replace Abbreviation In Given String

我正在处理一项要求,地址文本中的北、南、东、西应替换为 N、S、E、W

我已经在试用以下模板

<xsl:template name="string-replace-all">
<xsl:param name="text"/>
<xsl:param name="replace"/>
<xsl:param name="by"/>
<xsl:choose>
  <xsl:when test="contains($text, $replace)">
    <xsl:value-of select="substring-before($text,$replace)"/>
    <xsl:value-of select="$by"/>
    <xsl:call-template name="string-replace-all">
      <xsl:with-param name="text" select="substring-after($text,$replace)"/>
      <xsl:with-param name="replace" select="$replace"/>
      <xsl:with-param name="by" select="$by"/>
    </xsl:call-template>
  </xsl:when>
  <xsl:otherwise>
    <xsl:value-of select="$text"/>
  </xsl:otherwise>
</xsl:choose>

上述模板的问题是我必须在同一文本上一遍又一遍地重复此模板 4 次。我正在寻找一些全面的解决方案。有的话请告诉我。

来源XML

<sam:process>
<sam:DETAIL>
    <sam:CITY>Hicksville</sam:CITY>
    <sam:ZIP>11801</sam:ZIP>
    <sam:ADDR1>Old Country Road North</sam:ADDR1>
</sam:DETAIL>
<sam:DETAIL>
    <sam:CITY>Hicksville</sam:CITY>
    <sam:ZIP>11801</sam:ZIP>
    <sam:ADDR1>Old Country Road SOUTH EAST</sam:ADDR1>
</sam:DETAIL>
<sam:DETAIL>
    <sam:CITY>Hicksville</sam:CITY>
    <sam:ZIP>11801</sam:ZIP>
    <sam:ADDR1>Old Country Road North EAST</sam:ADDR1>
</sam:DETAIL>   

预期XML输出

<sam:process>
<sam:DETAIL>
    <sam:CITY>Hicksville</sam:CITY>
    <sam:ZIP>11801</sam:ZIP>
    <sam:ADDR1>Old Country Road N</sam:ADDR1>
</sam:DETAIL>
<sam:DETAIL>
    <sam:CITY>Hicksville</sam:CITY>
    <sam:ZIP>11801</sam:ZIP>
    <sam:ADDR1>Old Country Road SE</sam:ADDR1>
</sam:DETAIL>
<sam:DETAIL>
    <sam:CITY>Hicksville</sam:CITY>
    <sam:ZIP>11801</sam:ZIP>
    <sam:ADDR1>Old Country Road NE</sam:ADDR1>
</sam:DETAIL>   

最接近的版本XSLT 1.0:

来源

<sam:process xmlns:sam="baum">
    <sam:DETAIL>
        <sam:CITY>Hicksville</sam:CITY>
        <sam:ZIP>11801</sam:ZIP>
        <sam:ADDR1>Old Country Road North</sam:ADDR1>
    </sam:DETAIL>
    <sam:DETAIL>
        <sam:CITY>Hicksville</sam:CITY>
        <sam:ZIP>11801</sam:ZIP>
        <sam:ADDR1>Old Country Road SOUTH EAST</sam:ADDR1>
    </sam:DETAIL>
    <sam:DETAIL>
        <sam:CITY>Hicksville</sam:CITY>
        <sam:ZIP>11801</sam:ZIP>
        <sam:ADDR1>Old Country Road North EAST</sam:ADDR1>
    </sam:DETAIL>
    <sam:DETAIL>
        <sam:CITY>Hicksville</sam:CITY>
        <sam:ZIP>11801</sam:ZIP>
        <sam:ADDR1>Northwood Road North EAST</sam:ADDR1>
    </sam:DETAIL>
    <sam:DETAIL>
        <sam:CITY>Hicksville</sam:CITY>
        <sam:ZIP>11801</sam:ZIP>
        <sam:ADDR1>Southfield Road South WEst</sam:ADDR1>
    </sam:DETAIL>
</sam:process>

结果

<sam:process xmlns:sam="baum">
    <sam:DETAIL>
        <sam:CITY>Hicksville</sam:CITY>
        <sam:ZIP>11801</sam:ZIP>
        <sam:ADDR1>Old Country Road N</sam:ADDR1>
    </sam:DETAIL>
    <sam:DETAIL>
        <sam:CITY>Hicksville</sam:CITY>
        <sam:ZIP>11801</sam:ZIP>
        <sam:ADDR1>Old Country Road S E</sam:ADDR1>
    </sam:DETAIL>
    <sam:DETAIL>
        <sam:CITY>Hicksville</sam:CITY>
        <sam:ZIP>11801</sam:ZIP>
        <sam:ADDR1>Old Country Road N E</sam:ADDR1>
    </sam:DETAIL>
    <sam:DETAIL>
        <sam:CITY>Hicksville</sam:CITY>
        <sam:ZIP>11801</sam:ZIP>
        <sam:ADDR1>Northwood Road N E</sam:ADDR1>
    </sam:DETAIL>
    <sam:DETAIL>
        <sam:CITY>Hicksville</sam:CITY>
        <sam:ZIP>11801</sam:ZIP>
        <sam:ADDR1>Southfield Road S W</sam:ADDR1>
    </sam:DETAIL>
</sam:process>

样式表

<xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:sam="baum">

    <xsl:variable name="upperChars" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
    <xsl:variable name="lowerChars" select="'abcdefghijklmnopqrstuvwxyz'"/>

    <!-- identity copy template -->
    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>

    <!-- change this matching node -->
    <xsl:template match="sam:ADDR1">
        <xsl:copy>
            <xsl:call-template name="replaceRules">
                <!-- wrap the string with spaces!
                    important, otherwise some searchterms wont be found if they are at the beginning or end -->
                <xsl:with-param name="string" select="concat(' ', ., ' ')"/>
            </xsl:call-template> 
        </xsl:copy>
    </xsl:template>

    <!-- template to 'rule them all' -->
    <xsl:template name="replaceRules">
        <xsl:param name="string"/><!-- original string -->

        <!-- change from upper to lower case, to search just once for each searchterm - more or less case insensitiv -->
        <xsl:variable name="lowerString" select="translate($string, $upperChars, $lowerChars)" />

        <!-- rules -->
        <xsl:choose>
            <xsl:when test="contains($lowerString, ' north ')">
                <xsl:call-template name="replaceRules">
                    <xsl:with-param name="string">
                        <xsl:call-template name="replaceRule">
                            <xsl:with-param name="string" select="$string"/>
                            <xsl:with-param name="searchstring" select="$lowerString"/>
                            <xsl:with-param name="searchterm" select="' north '"/>
                            <xsl:with-param name="replacement" select="' N '" />
                        </xsl:call-template>                        
                    </xsl:with-param>
                </xsl:call-template>
            </xsl:when>
            <xsl:when test="contains($lowerString, ' east ')">
                <xsl:call-template name="replaceRules">
                    <xsl:with-param name="string">
                        <xsl:call-template name="replaceRule">
                            <xsl:with-param name="string" select="$string"/>
                            <xsl:with-param name="searchstring" select="$lowerString"/>
                            <xsl:with-param name="searchterm" select="' east '"/>
                            <xsl:with-param name="replacement" select="' E '" />
                        </xsl:call-template>                        
                    </xsl:with-param>
                </xsl:call-template>
            </xsl:when>
            <xsl:when test="contains($lowerString, ' south ')">
                <xsl:call-template name="replaceRules">
                    <xsl:with-param name="string">
                        <xsl:call-template name="replaceRule">
                            <xsl:with-param name="string" select="$string"/>
                            <xsl:with-param name="searchstring" select="$lowerString"/>
                            <xsl:with-param name="searchterm" select="' south '"/>
                            <xsl:with-param name="replacement" select="' S '" />
                        </xsl:call-template>                        
                    </xsl:with-param>
                </xsl:call-template>
            </xsl:when>
            <xsl:when test="contains($lowerString, ' west ')">
                <xsl:call-template name="replaceRules">
                    <xsl:with-param name="string">
                        <xsl:call-template name="replaceRule">
                            <xsl:with-param name="string" select="$string"/>
                            <xsl:with-param name="searchstring" select="$lowerString"/>
                            <xsl:with-param name="searchterm" select="' west '"/>
                            <xsl:with-param name="replacement" select="' W '" />
                        </xsl:call-template>                        
                    </xsl:with-param>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <!-- remove leading and trailing spaces -->
                <xsl:value-of select="normalize-space($string)"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

    <!-- applying one single rule to string - not more unusual replacement function needed, cause i search in a lower case string BUT get substring from the original string-->
    <xsl:template name="replaceRule">
        <xsl:param name="string" /> <!-- original text -->
        <xsl:param name="searchstring" /><!-- lower case text -->
        <xsl:param name="searchterm" /><!-- search for xyz -->
        <xsl:param name="replacement" /><!-- replace xyz with -->

        <xsl:variable name="lengthAfter" select="string-length(substring-after($searchstring, $searchterm))"/>

        <xsl:value-of select="
            concat(
                substring(
                    $string,
                    1,
                    string-length(substring-before($searchstring, $searchterm))
                ),
                $replacement,
                substring(
                    $string,
                    string-length($searchstring) - $lengthAfter + 1,
                    $lengthAfter
                )
            )"/>
    </xsl:template>
</xsl:stylesheet>