3

我想用另一个字符串替换一个字符串。我找到了一个例子,但似乎没有用。这是一个样本数据

<Addy>
  <Row>
  <LD>Dwelling, 1</D>
  <LN> East</LN>
  <L>1</L>
  <Tf>Abesinia Passage</Tf>
  </Row>

  <Row>
  <LD>Logde</LD>
  <LN>North </LN>
  <L>1</L>
  <Tf>Abesinia Passage</Tf>
  </Row>
</Addy>

我想以这种方式替换以下字符串。

 Dwelling = FLAT
 Lodge    =  SHOP

以下是我使用的代码。它只删除了 LD 元素中的所有值。

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:lookup="lookup">

<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>

<lookup:data>
    <LD code="Dwelling">FLAT</LD>
     <LD code="Lodge">SHOP</LD>

</lookup:data>

<xsl:variable name="lookup" select="document('')/*/lookup:data"/>

<xsl:template match="LD/text()">
    <xsl:value-of select="$lookup/LD[@code = current()]" />
</xsl:template>

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

如果应用于上面的输入数据,它会产生:

   <Addy>
  <Row>
  <LD></LD>
  <LN> East</LN>
  <L>1</L>
  <Tf>Abesinia Passage</Tf>
  </Row>

  <Row>
  <LD></LD>
  <LN>North </LN>
  <L>1</L>
  <Tf>Abesinia Passage</Tf>
  </Row>
  </Addy>

应产生具有适当代码的预期结果

   <Addy>
  <Row>
  <LD>FLAT,1</D>
  <LN> East</LN>
  <L>1</L>
  <Tf>Abesinia Passage</Tf>
  </Row>

  <Row>
  <LD>SHOP</LD>
  <LN>North </LN>
  <L>1</L>
  <Tf>Abesinia Passage</Tf>
  </Row>
  </Addy>  
4

3 回答 3

2

这是一个 XSLT 转换,用于对字符串执行多次替换——不需要扩展函数

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:my="my:my">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <my:reps>
  <rep>
   <old>Dwelling</old>
   <new>FLAT</new>
  </rep>
  <rep>
   <old>Lodge</old>
   <new>SHOP</new>
  </rep>
 </my:reps>

 <xsl:variable name="vReps" select="document('')/*/my:reps/*"/>

 <xsl:output omit-xml-declaration="yes" indent="yes"/>

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

 <xsl:template match="LD/text()" name="replace">
  <xsl:param name="pText" select="."/>

  <xsl:choose>
   <xsl:when test="not($vReps/old[contains($pText, .)])">
    <xsl:value-of select="$pText"/>
   </xsl:when>
   <xsl:otherwise>
       <xsl:call-template name="multiReplace">
        <xsl:with-param name="pText" select="$pText"/>
        <xsl:with-param name="pReps"
         select="$vReps[contains($pText, old)]"/>
       </xsl:call-template>
   </xsl:otherwise>
  </xsl:choose>
 </xsl:template>

 <xsl:template name="multiReplace">
  <xsl:param name="pText"/>
  <xsl:param name="pReps"/>

  <xsl:choose>
      <xsl:when test="$pReps">
       <xsl:variable name="vRepResult">
         <xsl:call-template name="singleReplace">
           <xsl:with-param name="pText" select="$pText"/>
           <xsl:with-param name="pOld" select="$pReps[1]/old"/>
           <xsl:with-param name="pNew" select="$pReps[1]/new"/>
         </xsl:call-template>
       </xsl:variable>

       <xsl:call-template name="multiReplace">
        <xsl:with-param name="pText" select="$vRepResult"/>
        <xsl:with-param name="pReps" select="$pReps[position() >1]"/>
       </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
       <xsl:value-of select="$pText"/>
      </xsl:otherwise>
  </xsl:choose>
 </xsl:template>

 <xsl:template name="singleReplace">
  <xsl:param name="pText"/>
  <xsl:param name="pOld"/>
  <xsl:param name="pNew"/>

  <xsl:if test="$pText">
   <xsl:choose>
    <xsl:when test="not(contains($pText, $pOld))">
     <xsl:value-of select="$pText"/>
    </xsl:when>
    <xsl:otherwise>
     <xsl:value-of select="substring-before($pText, $pOld)"/>
     <xsl:value-of select="$pNew"/>
     <xsl:call-template name="singleReplace">
       <xsl:with-param name="pText" select="substring-after($pText, $pOld)"/>
       <xsl:with-param name="pOld" select="$pOld"/>
       <xsl:with-param name="pNew" select="$pNew"/>
     </xsl:call-template>
    </xsl:otherwise>
   </xsl:choose>
  </xsl:if>
 </xsl:template>
</xsl:stylesheet>

当此转换应用于提供的 XML 文档时:

<Addy>
    <Row>
        <LD>Dwelling, 1</LD>
        <LN> East</LN>
        <L>1</L>
        <Tf>Abesinia Passage</Tf>
    </Row>
    <Row>
        <LD>Lodge</LD>
        <LN>North </LN>
        <L>1</L>
        <Tf>Abesinia Passage</Tf>
    </Row>
</Addy>

产生了想要的正确结果:

<Addy>
   <Row>
      <LD>FLAT, 1</LD>
      <LN> East</LN>
      <L>1</L>
      <Tf>Abesinia Passage</Tf>
   </Row>
   <Row>
      <LD>SHOP</LD>
      <LN>North </LN>
      <L>1</L>
      <Tf>Abesinia Passage</Tf>
   </Row>
</Addy>

重要

该解决方案是完整且正确的。肖恩的比较肤浅。

当应用于以下 XML 文档时,比较两种解决方案的结果

<Addy>
    <Row>
        <LD>Dwelling, Lodge, 1</LD>
        <LN> East</LN>
        <L>1</L>
        <Tf>Abesinia Passage</Tf>
    </Row>
    <Row>
        <LD>Lodge, Dwelling</LD>
        <LN>North </LN>
        <L>1</L>
        <Tf>Abesinia Passage</Tf>
    </Row>
</Addy>

肖恩的解决方案产生了不正确的替换

<Addy>
   <Row>
      <LD>FLAT, Lodge, 1</LD>
      <LN> East</LN>
      <L>1</L>
      <Tf>Abesinia Passage</Tf>
   </Row>
   <Row>
      <LD>Lodge, FLAT</LD>
      <LN>North </LN>
      <L>1</L>
      <Tf>Abesinia Passage</Tf>
   </Row>
</Addy>

答案的当前正确解决方案会产生正确的替换:

<Addy>
   <Row>
      <LD>FLAT, SHOP, 1</LD>
      <LN> East</LN>
      <L>1</L>
      <Tf>Abesinia Passage</Tf>
   </Row>
   <Row>
      <LD>SHOP, FLAT</LD>
      <LN>North </LN>
      <L>1</L>
      <Tf>Abesinia Passage</Tf>
   </Row>
</Addy>

说明

  1. 身份规则“按原样”复制每个被选择执行的匹配节点。

  2. 它被一个匹配任何元素的任何文本节点子节点的单个模板覆盖LD——必须在其中进行替换的节点。

  3. 此模板检查匹配的文本节点是否包含全局内联元素old中指定的任何(字符串值) 。my:reps为方便起见,所有my:reps/rep元素都已在名为的全局变量中选择,$vReps并从该变量中引用。如果当前节点中不包含这些字符串,则将其复制到输出。

  4. 如果当前匹配的文本节点中至少有一个$vReps/old元素的字符串值包含,那么我们必须进行替换。我们调用一个带有名称的模板"multiReplace"来执行当前文本节点中的所有替换。我们将当前文本节点和所有$vReps/rep元素的节点集作为参数传递给该模板,其子节点的字符串值old包含在当前文本节点中——这些都是要进行的替换。

  5. multiReplace模板调用名为的模板进行singleReplace第一次替换,并将结果捕获到名为 的变量中$vRepResult。这包含用 的字符串值替换$pText 所有出现的(的字符串值)$pReps[1]/old的结果$pReps[1]/new。然后,multiReplace模板递归调用自身,并将到目前为止的替换结果作为$pText参数传递,以及排除第一个替换的要进行的替换的节点集——作为$pReps参数。此递归的“停止条件”是当$pReps参数变为空节点集时。

  6. 模板按照singleReplace它的名称执行 - 它用参数中包含的字符串替换其参数中包含的字符串中与$pText参数相等的任何子字符串。替换的数量可能大于一个,但它们都是针对单个替换规范 ==> 因此名称。替换再次以递归方式完成,当非空且仍包含时,停止条件。$pOldpNewsingleReplace$pText$pOld

于 2012-09-11T05:12:04.383 回答
0

现有代码的问题在于这一行

<xsl:value-of select="$lookup/LD[@code = current()]" />

仅当存在文本等于上下文节点的整个文本的 LD 元素时才会发出任何内容。所以谓词需要使用contains()而不是=.

使用 XSLT 2.0,您可以按如下方式更改此模板:

<xsl:template match="LD/text()">
    <xsl:variable name="LD" select="$lookup/LD[contains(current(), @code)]" />
    <xsl:value-of select="replace(., $LD/@code, $LD/text())" />
</xsl:template>

如果不能使用 XSLT 2.0,可以使用 EXSLT str:replace()代替 XSLT 2.0 的版本。

这假定code属性值不包含任何特殊字符,如.,$等,这些字符将在正则表达式中专门解释。

它还假设在任何 LD/text() 节点中不会出现超过一个代码。

于 2012-09-10T23:58:53.847 回答
0

LarsH 的解决方案是一个很好的解决方案。如果支持,请尝试使用 EXSLT。如果不支持并且您的 XSLT 引擎是 Microsoft,那么这个 XSLT 1.0 样式表...

<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
  xmlns:l="http://stackoverflow.com/questions/12360735"
  xmlns:msxsl="urn:schemas-microsoft-com:xslt"
  exclude-result-prefixes="xsl l msxsl" >
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:variable name="lookup">
  <l:map pattern="Dwelling" replacement="FLAT" />
  <l:map pattern="Lodge"    replacement="SHOP" />
</xsl:variable>  

<xsl:template match="LD/text()">
  <xsl:choose>
    <xsl:when test="contains(.,msxsl:node-set($lookup)/l:map/@pattern)">
      <xsl:variable name="hay-stack" select="." />
      <xsl:for-each select="(msxsl:node-set($lookup)/l:map[contains($hay-stack,@pattern)])[1]">
        <xsl:value-of select="concat(
          substring-before($hay-stack,@pattern),
          @replacement,
          substring-after($hay-stack,@pattern))" />
      </xsl:for-each>  
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="." />
    </xsl:otherwise>  
  </xsl:choose>  
</xsl:template>

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

...当应用于此输入时...

<Addy>
  <Row>
  <LD>Dwelling, 1</LD>
  <LN> East</LN>
  <L>1</L>
  <Tf>Abesinia Passage</Tf>
  </Row>

  <Row>
  <LD>Lodge</LD>
  <LN>North </LN>
  <L>1</L>
  <Tf>Abesinia Passage</Tf>
  </Row>
</Addy>

...产量...

<Addy>
  <Row>
    <LD>FLAT, 1</LD>
    <LN> East</LN>
    <L>1</L>
    <Tf>Abesinia Passage</Tf>
  </Row>
  <Row>
    <LD>Lodge</LD>
    <LN>North </LN>
    <L>1</L>
    <Tf>Abesinia Passage</Tf>
  </Row>
</Addy>

如果不是 Microsoft,并且您不能使用 EXSLT,请使用此样式表...

<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
  xmlns:l="http://stackoverflow.com/questions/12360735"
  exclude-result-prefixes="xsl l" >
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:variable name="lookup">
  <l:map pattern="Dwelling" replacement="FLAT" />
  <l:map pattern="Lodge"    replacement="SHOP" />
</xsl:variable>  

<xsl:template match="LD/text()">
  <xsl:choose>
    <xsl:when test="contains(.,document('')/*/xsl:variable[@name="lookup"]/l:map/@pattern)">
      <xsl:variable name="hay-stack" select="." />
      <xsl:for-each select="(document('')/*/xsl:variable[@name="lookup"]/l:map[contains($hay-stack,@pattern)])[1]">
        <xsl:value-of select="concat(
          substring-before($hay-stack,@pattern),
          @replacement,
          substring-after($hay-stack,@pattern))" />
      </xsl:for-each>  
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="." />
    </xsl:otherwise>  
  </xsl:choose>  
</xsl:template>

<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>
</xsl:stylesheet>
于 2012-09-11T02:00:33.267 回答