Home > Back-end >  Parse WMS XML response and return an object containing key-value pairs
Parse WMS XML response and return an object containing key-value pairs

Time:03-11

Coming off Using a Javascript DOM Parser extract the list of Layers from the XML response.data of an WMS GetCapabilities request I am trying to implement an xpath that would take the XML response example and return an object that contains the key-value pairs for the leaf Layer tag including Name, Title, Abstract, Dimension : [Dimension_time, Dimension_reference_time], Style: [Styles Names, Titles and URLs] so for the above link :

Name: 'CGSL.ETA_ICEC',
Title: 'CGSL.ETA.ICEC - Fraction de glace',
Abstract: 'Le Système régional de prévision déterministe (SRPD) procède à ...',
Dimension: {
    Dimension_time: '2022-03-09T13:00:00Z/2022-03-11T12:00:00Z/PT1H',
    Dimension_ref_time: '2022-03-08T06:00:00Z/2022-03-09T12:00:00Z/PT6H'
},
Style: [
    {
        Name: 'SEA_ICECONC-LINEAR',
        Title: 'SEA_ICECONC-LINEAR',
        LegendWidth: 82,
        LegendHeight: 155,
        LegendURL: 'https://geo.weather.gc.ca/geomet?lang=fr&version=1.3.0&service=WMS&request=GetLegendGraphic&sld_version=1.1.0&layer=CGSL.ETA_ICEC&format=image/png&STYLE=SEA_ICECONC-LINEAR'
    },
    {
        Name: 'SEA_ICECONC',
        Title: 'SEA_ICECONC',
        LegendWidth: 82,
        LegendHeight: 155,
        LegendURL: 'https://geo.weather.gc.ca/geomet?lang=fr&version=1.3.0&service=WMS&request=GetLegendGraphic&sld_version=1.1.0&layer=CGSL.ETA_ICEC&format=image/png&STYLE=SEA_ICECONC'
    }
]

And my application runs in the browser and I have access to SaxonJS but if the native DomParser can do the trick it would remove a dependency for me.

axios.get('https://geo.weather.gc.ca/geomet/?service=WMS&version=1.3.0&request=GetCapabilities&LANG=en&LAYER=CGSL.ETA_ICEC')
.then((response) => {
const xslt =`XSLT`
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(tempObject);

EDIT : Thank you Mr. Martin Honnen

Mr. Honnen has the exact solution I was looking for and I think I understand better how SaxonJS works thanks to him. People like him are the backbone of programming and I hope I get to help others like he does one day.

CodePudding user response:

With XPath a Layer not containg another Layer would be selected by Layer[not(.//Layer)] (ignoring namespaces, for the time).

With Saxon-JS and XPath 3.1, to have JavaScript objects and arrays on the JavaScript side, you can use XPath 3.1 maps (https://www.w3.org/TR/xpath-31/#id-maps) and arrays (https://www.w3.org/TR/xpath-31/#id-arrays):

const result = SaxonJS.XPath.evaluate(`doc($url)//Layer[not(.//Layer)]!map {
  'Name' : string(Name),
  'Title' : string(Title),
  'Abstract' : string(Abstract),
  'Dimension' : map {
    'Dimension_time' : string(Dimension[@name = 'time']),
    'Dimension_ref_time' : string(Dimension[@name = 'reference_time'])
  },
  'Style' : array { Style !
    map {
      'Name' : string(Name),
      'Title' : string(Title),
      'LegendWith' : string(LegendURL/@width),
      'LegendHeight' : string(LegendURL/@height),
      'LegendURL' : string(LegendURL/OnlineResource/@xlink:href)
    }
  }
}`, null, { xpathDefaultNamespace : 'http://www.opengis.net/wms', namespaceContext : { xlink : 'http://www.w3.org/1999/xlink' }, params : { url : 'https://geo.weather.gc.ca/geomet/?service=WMS&version=1.3.0&request=GetCapabilities&LANG=en&LAYER=CGSL.ETA_ICEC' } });

console.log(result);
      
    
  
<script src="https://www.saxonica.com/saxon-js/documentation2/SaxonJS/SaxonJS2.rt.js"></script>

The only drawback of relying on XPath 3.1 maps is that they are unordered so in the end the result might look like

{
  "Abstract": "The Regional Deterministic Prediction System (RDPS) carries out physics calculations to arrive at deterministic predictions of atmospheric elements from the current day out to 84 hours into the future. Atmospheric elements include temperature, precipitation, cloud cover, wind speed and direction, humidity and others. This product contains raw numerical results of these calculations. Geographical coverage includes Canada and the United States. Data is available at horizontal resolution of about 10 km up to 33 vertical levels. Predictions are performed four times a day.",
  "Dimension": {
    "Dimension_time": "2022-03-10T13:00:00Z/2022-03-12T12:00:00Z/PT1H",
    "Dimension_ref_time": "2022-03-09T06:00:00Z/2022-03-10T12:00:00Z/PT6H"
  },
  "Name": "CGSL.ETA_ICEC",
  "Title": "CGSL.ETA.ICEC - Ice cover fraction",
  "Style": [
    {
      "LegendWith": "82",
      "LegendURL": "https://geo.weather.gc.ca/geomet?version=1.3.0&service=WMS&request=GetLegendGraphic&sld_version=1.1.0&layer=CGSL.ETA_ICEC&format=image/png&STYLE=SEA_ICECONC-LINEAR",
      "LegendHeight": "155",
      "Name": "SEA_ICECONC-LINEAR",
      "Title": "SEA_ICECONC-LINEAR"
    },
    {
      "LegendWith": "82",
      "LegendURL": "https://geo.weather.gc.ca/geomet?version=1.3.0&service=WMS&request=GetLegendGraphic&sld_version=1.1.0&layer=CGSL.ETA_ICEC&format=image/png&STYLE=SEA_ICECONC",
      "LegendHeight": "155",
      "Name": "SEA_ICECONC",
      "Title": "SEA_ICECONC"
    }
  ]
}

i.e. the objects might have their properties in a different order than in the wanted result.

If you have the XML as a string, as your response.data, use parse-xml e.g.

axios.get('https://geo.weather.gc.ca/geomet/?service=WMS&version=1.3.0&request=GetCapabilities&LANG=en&LAYER=CGSL.ETA_ICEC')
.then((response) => {
        const result = SaxonJS.XPath.evaluate(`parse-xml($xml)//Layer[not(.//Layer)]!map {
      'Name' : string(Name),
      'Title' : string(Title),
      'Abstract' : string(Abstract),
      'Dimension' : map {
        'Dimension_time' : string(Dimension[@name = 'time']),
        'Dimension_ref_time' : string(Dimension[@name = 'reference_time'])
      },
      'Style' : array { Style !
        map {
          'Name' : string(Name),
          'Title' : string(Title),
          'LegendWith' : string(LegendURL/@width),
          'LegendHeight' : string(LegendURL/@height),
          'LegendURL' : string(LegendURL/OnlineResource/@xlink:href)
        }
      }
    }`, null, { xpathDefaultNamespace : 'http://www.opengis.net/wms', namespaceContext : { xlink : 'http://www.w3.org/1999/xlink' }, params : { xml: response.data } });

      })
  • Related