Using a Map in XSL for expanding abbreviations

≡放荡痞女 提交于 2019-12-24 01:19:29

问题


I saw a similar question on creating a Map.

That answer has this code:

<xsl:stylesheet version="1.0" 
            xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
            xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<xsl:template match="/">
    <xsl:variable name="map">
        <map>
            <entry key="key-1">value1</entry>
            <entry key="key-2">value2</entry>
            <entry key="key-3">value3</entry>
        </map>
    </xsl:variable>
    <output>
        <xsl:value-of select="msxsl:node-set($map)/map/entry[@key='key-1']"/>
    </output>
</xsl:template>

I would like to replace the output command to use a value in my XML to see if it is a key in the map and then replace it with the value.

Is the best way to do a for-each select on the map and compare with contains?

Here is a snippet of the XML:

<document>
   <content name="PART_DESC_SHORT" type="text" vse-streams="2" u="22" action="cluster" weight="1">
   SCREW - ADJUST
   </content>
</document>

The content node value may have a string containing an abbreviation that I want to replace with the full value.

Thanks, Paul


回答1:


No need to use a for-each - this XSLT:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt">

  <xsl:output indent="yes" method="xml" />

  <xsl:variable name="abbreviations">
    <abbreviation key="Brkt Pivot R">Bracket Pivot R</abbreviation>
    <abbreviation key="Foo">Expanded Foo</abbreviation>
    <abbreviation key="Bar">Expanded Bar</abbreviation>
  </xsl:variable>

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

  <xsl:template match="text()">
    <xsl:variable name="text" select="."/>
    <xsl:variable name="abbreviation" select="msxsl:node-set($abbreviations)/*[@key=$text]"/>
    <xsl:choose>
      <xsl:when test="$abbreviation">
        <xsl:value-of select="$abbreviation"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:copy/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

will convert any XML in an exact copy expanding all the text matching an abbreviation defined in the abbreviations variable at the top.

If you want to expand the abbreviations only within specific elements you can modify the second template match="..." rule.

On the other hand if you want to expand ANY occurance of all the abbreviations within the text you need loops - that means recursion in XSLT:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt">

  <xsl:output indent="yes" method="xml" />

  <xsl:variable name="abbreviations">
    <abbreviation key="Brkt">Bracket</abbreviation>
    <abbreviation key="As">Assembly</abbreviation>
    <abbreviation key="Foo">Expanded Foo</abbreviation>
    <abbreviation key="Bar">Expanded Bar</abbreviation>
  </xsl:variable>

  <!-- Replaces all occurrences of a string with another within a text -->
  <xsl:template name="replace">
    <xsl:param name="text"/>
    <xsl:param name="from"/>
    <xsl:param name="to"/>
    <xsl:choose>
      <xsl:when test="contains($text,$from)">
        <xsl:value-of select="concat(substring-before($text,$from),$to)"/>
        <xsl:call-template name="replace">
          <xsl:with-param name="text" select="substring-after($text,$from)"/>
          <xsl:with-param name="from" select="$from"/>
          <xsl:with-param name="to" select="$to"/>
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$text"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  <!-- Replace all occurences of a list of abbreviation with their expanded version -->
  <xsl:template name="replaceAbbreviations">
    <xsl:param name="text"/>
    <xsl:param name="abbreviations"/>
    <xsl:choose>
      <xsl:when test="count($abbreviations)>0">
        <xsl:call-template name="replaceAbbreviations">
          <xsl:with-param name="text">
            <xsl:call-template name="replace">
              <xsl:with-param name="text" select="$text"/>
              <xsl:with-param name="from" select="$abbreviations[1]/@key"/>
              <xsl:with-param name="to" select="$abbreviations[1]"/>
            </xsl:call-template>
          </xsl:with-param>
          <xsl:with-param name="abbreviations" select="$abbreviations[position()>1]"/>
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$text"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

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

  <xsl:template match="text()">
    <xsl:call-template name="replaceAbbreviations">
      <xsl:with-param name="text" select="."/>
      <xsl:with-param name="abbreviations" select="msxsl:node-set($abbreviations)/*"/>
    </xsl:call-template>
  </xsl:template>

</xsl:stylesheet>

Applying this second XSLT to

<document>
  <content name="PART_DESC_SHORT" type="text" vse-streams="2" u="22" action="cluster" weight="1">
    Brkt Pivot R
  </content>
</document>

produces

<document>
  <content name="PART_DESC_SHORT" type="text" vse-streams="2" u="22" action="cluster" weight="1">
    Bracket Pivot R
  </content>
</document>

Note that:

  • this solution assumes that no abbreviation ovelap (e.g. two separate abbreviations Brk and Brkt)

  • it uses XSLT 1.0 - a better solution is probably possible with XSLT 2.0

  • this kind of heavy string processing is likely quite inefficient in XSLT, it is probably better to write an extension function in some other language and call it from the XSLT.



来源:https://stackoverflow.com/questions/10521203/using-a-map-in-xsl-for-expanding-abbreviations

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