skip to Main Content

ish at XSLT and have a XSLT output that contains some json within elements.

I’ve been looking around to see if there is anyway for XSLT to replace the json with just a string:

Ex:

<company:Cause 
   xmlns:company="http://example.com/company" 
   xmlns:j="http://example.com/j" 
   xmlns:s="http://example.com/s" 
   xmlns:nc="http://example.com/nc"
>
   <j:Cause s:id="Cause-08D628B86EF2">
      <nc:DescriptionText>{"code":"PC","description":"Probable Cause"}</nc:DescriptionText>
      <nc:ActivityDate>
         <nc:DateTime>2018-10-02T00:00:00</nc:DateTime>
      </nc:ActivityDate>
      <j:Name>{"code":"PC","description":"Probable Cause"}</j:Name>
      <company:Augmentation>
         <j:Stat>
            <j:DescriptionText>{"code":"PC","description":"Probable Cause"}</j:DescriptionText>
         </j:Stat>
      </company:Augmentation>
   </j:Cause>
</company:Cause>

Is there anything I can add to the XSLT in order to do this on the fly? Anything already built into XSLT?

2

Answers


  1. XSLT 3 can parse/process JSON:

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      version="3.0"
      xmlns:xs="http://www.w3.org/2001/XMLSchema"
      xmlns:fn="http://www.w3.org/2005/xpath-functions"
      xmlns:mf="http://example.com/mf"
      exclude-result-prefixes="#all"
      expand-text="yes">
    
      <xsl:function name="mf:try-json-to-xml" as="xs:boolean">
        <xsl:param name="json" as="xs:string"/>
        <xsl:try>
          <xsl:sequence select="if (json-to-xml($json)) then true() else false()"/>
          <xsl:catch select="false()"/>
        </xsl:try>
      </xsl:function>
    
      <xsl:mode on-no-match="shallow-copy"/>
      
      <xsl:mode name="text"/>
      
      <xsl:template mode="text" match="fn:*[@key and not(*)]">{@key} {.}{if (position() lt last()) then ' ' else ()}</xsl:template>
      
      <xsl:template match="*[not(*) and mf:try-json-to-xml(.)]">
        <xsl:copy>
          <xsl:apply-templates select="json-to-xml(.)" mode="text"/>
        </xsl:copy>
      </xsl:template>
    
    </xsl:stylesheet>
    

    Example fiddle running SaxonJS with pure JavaScript in the browser.

    Example fiddle running Saxon HE 12 Java with CheerpJ 3 in the browser.

    XSLT 3 has been the current version of XSLT since 2017 and is currently supported on a variety of platforms through free to use libraries like e.g. Saxon HE 12 Java for Java, SaxonC 12 for C/C++ and Python and PHP, SaxonJS 2.6 for JavaScript and Node.js and Saxon HE 10 .NET for the .NET framework or .NET 6/8.

    There are also commercial offerings like Altova RaptorXML or SaxonCS.

    Login or Signup to reply.
  2. If you’re stuck with XSLT 1.0, and you wanted to process JSON properly, tt would certainly be possible to build a full JSON parser using recursive named templates, but that may be overkill. Much simpler to do as y.arazim suggests and just strip out the "special" JSON characters by translating them all into spaces, and then use the normalize-space() function to convert each run of multiple spaces into a single space. e.g.

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
       xmlns:company="http://example.com/company" 
       xmlns:j="http://example.com/j" 
       xmlns:s="http://example.com/s" 
       xmlns:nc="http://example.com/nc"
    >
    
      <!-- identity template copies anything that doesn't need munging -->
      <xsl:template match="*|@*">
        <xsl:copy>
          <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
      </xsl:template>
      
      <!-- these elements are munged to remove special JSON characters -->
      <xsl:template match="j:Name | j:DescriptionText | nc:DescriptionText">
        <xsl:variable name="unwanted-characters">{":,}</xsl:variable>
        <xsl:copy>
          <xsl:apply-templates select="@*"/>
          <xsl:value-of select="
            normalize-space(
              translate(., $unwanted-characters, '       ')
            )
          "/>
        </xsl:copy>
      </xsl:template>
    
    </xsl:stylesheet>
    

    NB the string of spaces which appears as the third parameter to the translate() function should be the same length (or longer!) than the second parameter (containing the unwanted characters). The translate() function takes the first parameter and for each character in that string it looks for that character in the second parameter, and if it’s found, it replaces it with the character at the same position in the third parameter. So the third parameter being entirely spaces means that every one of those unwanted characters will be replaced with a space.

    Result:

    <company:Cause xmlns:company="http://example.com/company" xmlns:j="http://example.com/j" xmlns:nc="http://example.com/nc" xmlns:s="http://example.com/s">
       <j:Cause s:id="Cause-08D628B86EF2">
          <nc:DescriptionText>code PC description Probable Cause</nc:DescriptionText>
          <nc:ActivityDate>
             <nc:DateTime>2018-10-02T00:00:00</nc:DateTime>
          </nc:ActivityDate>
          <j:Name>code PC description Probable Cause</j:Name>
          <company:Augmentation>
             <j:Stat>
                <j:DescriptionText>code PC description Probable Cause</j:DescriptionText>
             </j:Stat>
          </company:Augmentation>
       </j:Cause>
    </company:Cause>
    

    NB you will need to use the proper namespace URIs rather than the dummy values I added here.

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