jsonxmlxsltsaxonsaxon-js

XSLT WMS GetCapabilities to JSON


I am trying to convert the GetCapabilities of the GeoMet WMS XML response.data to a JSON that can be used in a v-treeview Vuetify component such as in this link.

testfunc: function () {
      axios.get('https://geo.weather.gc.ca/geomet?lang=en&service=WMS&version=1.3.0&request=GetCapabilities').then((response) => {
        const xslt = `<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="#all"
  xmlns="http://www.w3.org/2005/xpath-functions"
  xmlns:mf="http://example.com/mf"
  expand-text="yes"
    version="3.0">
  
  <xsl:strip-space elements="*"/>

  <xsl:output method="json" build-tree="no"/>
  
  <xsl:template match="/Layer" priority="5">
    <xsl:map>
      <xsl:apply-templates/>
    </xsl:map>
  </xsl:template>
  
  <xsl:template match="*[not(*)]">
    <xsl:map-entry key="local-name()" select="data()"/>
  </xsl:template>
  
  <xsl:template match="Layer[1]">
    <xsl:map-entry key="'children'">
      <xsl:sequence select="array { mf:apply-templates(*) }"/>
    </xsl:map-entry>
  </xsl:template>
  
  <xsl:template match="Layer[position() > 1]"/>
  
  <xsl:function name="mf:apply-templates" as="item()*">
    <xsl:param name="elements" as="element(*)*"/>
    <xsl:apply-templates select="$elements"/>
  </xsl:function>
  
</xsl:stylesheet>`
        const jsonResult = SaxonJS.XPath.evaluate(`
          transform(
            map { 
              'source-node' : parse-xml($xml), 
              'stylesheet-text' : $xslt, 
              'delivery-format' : 'raw' 
            }
            )?output`,
        [],
        { 'params': { 'xml': response.data, 'xslt': xslt } }
        )
        console.log(jsonResult)
      })
    }

My test function returns all the infromations and does not really parse the XML response the way I need it to and I am new to XSLT. I need something that will return only the <Name> and <Title> innerHTML of the <Layer> tags and their children in arrays called children that looks like :

{
title: 'Title Level 1'
name: 'Name Level 1'
children: [
    {
     title: 'Title Level 2'
     name: 'Name Level 2'
     children: [
         {
          title: 'Title Level 3-1'
          name: 'Name Level 3-1'
         },
         {
          title: 'Title Level 3-2'
          name: 'Name Level 3-2'
         }
     ]
]
}

EDIT : Sample XML of the full XML which has one root with only a title that has 14 groups of

<Layer queryable="1">
<Title>MSC GeoMet — GeoMet-Weather 2.14.1</Title>
   <Layer queryable="1">
   <Name>Regional Deterministic Prediction System (RDPS) [10 km]</Name>
   <Title>Regional Deterministic Prediction System (RDPS) [10 km]</Title>
      <Layer queryable="1">
      <Name>RDPS - Coupled to Gulf of St. Lawrence (RDPS-CGSL)</Name>
      <Title>RDPS - Coupled to Gulf of St. Lawrence (RDPS-CGSL)</Title>
         <Layer queryable="1" opaque="0" cascaded="0">
         <Name>CGSL.ETA_ICEC</Name>
         <Title>CGSL.ETA.ICEC - Ice cover fraction</Title>
...

Solution

  • An XSLT 3 sample to process only Layer elements (in that particular namespace http://www.opengis.net/wms) and the first Title and Name plus recursively the child Layers would be

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        xmlns:mf="http://example.com/mf"
        exclude-result-prefixes="#all"
        xpath-default-namespace="http://www.opengis.net/wms"
        xmlns="http://www.w3.org/2005/xpath-functions"
        expand-text="yes"
        version="3.0">
    
      <xsl:strip-space elements="*"/>
      
      <xsl:mode on-no-match="shallow-skip"/>
    
      <xsl:output method="json" build-tree="no" indent="yes"/>
      
      <xsl:template match="/WMS_Capabilities/Capability/Layer" priority="5">
        <xsl:map>
          <xsl:apply-templates/>
        </xsl:map>
      </xsl:template>
      
      <xsl:template match="Layer/Title[1] | Layer/Name[1]">
        <xsl:map-entry key="local-name()" select="data()"/>
      </xsl:template>
      
      <xsl:template match="Layer[1]">
        <xsl:map-entry key="'children'">
          <xsl:sequence select="array { ../Layer/mf:apply-templates(.) }"/>
        </xsl:map-entry>
      </xsl:template>
      
      <xsl:template match="Layer[position() > 1]"/>
      
      <xsl:function name="mf:apply-templates" as="item()*">
        <xsl:param name="elements" as="element(*)*"/>
        <xsl:map>
          <xsl:apply-templates select="$elements/*"/>      
        </xsl:map>
      </xsl:function>
      
    </xsl:stylesheet>