skip to Main Content

I have an XSLT implementation that hyphenates strings following latex patterns, i.e. inserts soft hyphens in strings. I call this function like this:

<xsl:template match="p[lang('en')]/text()">
  <xsl:analyze-string select="." regex="w+">
   <xsl:matching-substring>
    <xsl:value-of select="fn:hyphenate(., '­', 'en')"/>
   <xsl:non-matching-substring>
    <xsl:value-of select="."/>
   </xsl:non-matching-substring>
  </xsl:analyze-string>
 </xsl:template>

Now, before I call the hyphenate function, I would like to consult a list of exceptions. I’m fairly flexible about the format in which I get the exceptions, but I would also like to use this opportunity to learn how to take advantage of json objects from within XSLT 3.0.

So let’s say i have a JSON file called exceptions.json that looks like this:

{
    "en" :{
       "recognizance": "re-cog-ni-zance",
       "reformation": "ref-or-ma-tion",
       "retribution": "ret-ri-bu-tion",
       "table": "ta-ble"
    },

    "de" : {   
      //etc.   
    }
}

(I am not using soft hyphens in the above example because they wouldn’t show on Stackoverflow, but that’s irrelevant for the question I am asking).

How could I read (and parse?) the contents of the JSON file into a variable called $exceptions, so that I could most efficiently do something like:

  <xsl:template match="p[lang('en')]/text()">
  <xsl:analyze-string select="." regex="w+">
   <xsl:matching-substring>
    <xsl:choose>
     <xsl:when test="">
      <!--test if matched string is a key in my json object and return the 
          corresponding value. for instance, if the matched substring here is 
          "table", I'd like to return something like 
          <xsl:value-of select="$exceptions['en']['table']"/> , 
          i.e. "ta-ble", using whatever notation may be correct for this 
          kind of thing. -->
     </xsl:when>
     <xsl:otherwise>
      <xsl:value-of select="fn:hyphenate(., '­', 'en')"/>
     </xsl:otherwise>
    </xsl:choose>
    <xsl:non-matching-substring>
     <xsl:value-of select="."/>
    </xsl:non-matching-substring>
  </xsl:analyze-string>
 </xsl:template>

Many thanks in advance!

2

Answers


  1. Well, the XPath 3.1 and the XSLT 3.0 specs are online, so you could have found the necessary functions (https://www.w3.org/TR/xpath-functions-31/#func-json-doc, https://www.w3.org/TR/xpath-functions-31/#func-map-contains) and notations (https://www.w3.org/TR/xpath-31/#id-lookup).

    Use e.g. <xsl:variable name="exceptions" select="json-doc('exceptions.json')"/>, the you can access e.g. map:contains($exceptions?en, .) to check whether the current word is in the en map.

    Example: (reading JSON inline for self-containedness):

    <?xml version="1.0" encoding="utf-8"?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      version="3.0"
      xmlns:xs="http://www.w3.org/2001/XMLSchema"
      xmlns:map="http://www.w3.org/2005/xpath-functions/map"
      exclude-result-prefixes="#all"
      expand-text="yes">
    
      <xsl:output method="html" indent="yes" html-version="5"/>
      
      <xsl:template match="p[lang('en')]/text()">
      <xsl:analyze-string select="." regex="w+">
       <xsl:matching-substring>
        <xsl:choose>
         <xsl:when test="map:contains($exceptions?en, .)">
          <xsl:sequence select="$exceptions?en(.)"/>
         </xsl:when>
         <xsl:otherwise>
          <xsl:value-of select="."/>
         </xsl:otherwise>
        </xsl:choose>
        </xsl:matching-substring>
        <xsl:non-matching-substring>
         <xsl:value-of select="."/>
        </xsl:non-matching-substring>
      </xsl:analyze-string>
     </xsl:template>
     
      <xsl:mode on-no-match="shallow-copy"/>
    
      <xsl:template match="/" name="xsl:initial-template">
        <xsl:copy>
          <xsl:apply-templates/>
          <xsl:comment>Run with {system-property('xsl:product-name')} {system-property('xsl:product-version')} at {current-dateTime()}</xsl:comment>
        </xsl:copy>
      </xsl:template>
      
      <xsl:param name="json-exceptions" as="xs:string" expand-text="no">
    {
        "en" :{
           "recognizance": "re-cog-ni-zance",
           "reformation": "ref-or-ma-tion",
           "retribution": "ret-ri-bu-tion",
           "table": "ta-ble"
        }
    }    
      </xsl:param>
      
      <xsl:param name="exceptions" select="parse-json($json-exceptions)"/>
    
    </xsl:stylesheet>
    

    Online fiddle.

    Login or Signup to reply.
  2. Another option is to convert the JSON to XML, then process it as such.

    In your example, this could probably look something like:

    XSLT 3.0

    <xsl:stylesheet version="3.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:fn="http://www.w3.org/2005/xpath-functions"
    xmlns:your="your-namespace-here"
    exclude-result-prefixes="#all">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    
    <xsl:param name="exceptions" select="json-to-xml(unparsed-text('path/to/exceptions.json'))" />
    
    <xsl:key name="excpt" match="fn:string" use="@key" />
    
    <xsl:mode on-no-match="shallow-copy"/>
    
    <xsl:template match="p">
        <xsl:analyze-string select="." regex="w+">
            <xsl:matching-substring>
                <xsl:variable name="lookup" select="key('excpt', ., $exceptions)" />
                <xsl:value-of select="if($lookup) then $lookup else your:hyphenate(.)" /> 
            </xsl:matching-substring>
            <xsl:non-matching-substring>
                <xsl:value-of select="."/>
            </xsl:non-matching-substring>
        </xsl:analyze-string>
    </xsl:template>
    
    </xsl:stylesheet>
    

    Untested, because you did not provide a reproducible example. Do note that I am using the fn prefix as intended by the W3C Recommendation; you will need to use another prefix for your own function.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search