4

I'm trying to extract the headline from the below XML from the Met Office web service using XSLT, however my XSLT select returns blank.

SOURCE:

<RegionalFcst xmlns="www.metoffice.gov.uk/xml/metoRegionalFcst" createdOn="2016-01-13T02:14:39" issuedAt="2016-01-13T04:00:00" regionId="se">
 <FcstPeriods>
  <Period id="day1to2">
   <Paragraph title="Headline:">Frosty start. Bright or sunny day.</Paragraph>
   <Paragraph title="Today:">A clear and frosty start in west, but cloudier in Kent with isolated showers. Then dry with sunny periods. Increasing cloud in west later will bring coastal showers with freshening southerly winds. Chilly inland, but less cold near coasts. Maximum Temperature 8C.</Paragraph>
  </Period>
 </FcstPeriods>
</RegionalFcst>

My XSLT:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
  <html>
  <body>
   <xsl:value-of select="FcstPeriods/Period/Paragraph"/>
  </body>
  </html>
</xsl:template>
</xsl:stylesheet>

I've changed the root to /RegionalFcst and attempted other similar changes, such as adding a leading slash before FcstPeriods, but nothing works until I remove the first and last line from the source XML - then it works perfectly.

This is fine in testing, but of course I want to use the web service provided by Met Office and that's how they present it.

Any ideas?

hazymat
  • 404
  • 1
  • 6
  • 20

2 Answers2

16

The problem: your XML puts its elements in a namespace.

Solution: declare the same namespace in your stylesheet, assign it a prefix and use that prefix to address the elements in the source XML:

XSLT 1.0

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:met="www.metoffice.gov.uk/xml/metoRegionalFcst"
exclude-result-prefixes="met">
<xsl:template match="/">
  <html>
  <body>
   <xsl:value-of select="met:RegionalFcst/met:FcstPeriods/met:Period/met:Paragraph[@title='Headline:']"/>
  </body>
  </html>
</xsl:template>
</xsl:stylesheet>
michael.hor257k
  • 113,275
  • 6
  • 33
  • 51
  • Thanks this helped solving my problem, too. It should be emphasized that this only works for the default namespace "xmlns=...". If there are any other qualified namespaces in the source xml file they have to be added to "xsl:stylesheet", used in the xpath (without the temporary prefix for the default namespace), and added to "exclude-result-prefixes" (as space separated list). – Stefan Dolezel Jun 01 '17 at 09:10
  • @StefanDolezel No, this works for **any** namespaces used by the source XML. – michael.hor257k Jun 01 '17 at 09:28
  • Yes, it works the same way. I meant that only for the default namespace a prefix has to be _created_ ("met" in the example above). If the source file already has a qualified namespace, e.g. "xmlns:myns=...", one may just use this prefix in the xslt (or relabel it if it is more convenient). – Stefan Dolezel Jun 01 '17 at 12:05
  • Is there any alternative to this solution. The system I am working with doesn't support the extensions – Amir Oct 29 '20 at 13:18
  • @Amir What extensions? This is pure XPath/XSLT. – michael.hor257k Oct 29 '20 at 15:27
  • Sorry I was wrong, the error was comming from the setting in the version setting. By mistake i was using version 1 and since this is code is written for version 2 i was getting error. My bad! Your solution works perfectly. – Amir Nov 22 '20 at 11:59
  • @Amir Do note that the above is an XSLT 1.0 solution. If you're using XSLT 2.0 or higher, you can take advantage of `xpath-default-namespace`. – michael.hor257k Nov 22 '20 at 12:27
  • well funny because it didnt work till I changed the version to 2. Thanks, tomorrow I will try this solution. – Amir Nov 22 '20 at 13:04
1

Additional to the answer of "michael.hor257k", there is another solution, for the version 2.0 of XSLT.

XSLT 2.0

Use xpath-default-namespace attribute. For the example above it looks like this:

<xsl:stylesheet xpath-default-namespace="www.metoffice.gov.uk/xml/metoRegionalFcst" ... >

Then you don't need to repeat the namespace prefix in every element referenced by XPath:

<xsl:value-of select="FcstPeriods/Period/Paragraph"/>

instead of

<xsl:value-of select="met:FcstPeriods/met:Period/met:Paragraph"/>
30thh
  • 10,861
  • 6
  • 32
  • 42