Reputation: 163
I am trying to download and transform soap response from Magyar Nemzeti Bank. I managed to get the downloading part right, now I am having a problem with xslt transformation. I want to transform the message to xml so I can deserialize it.
The SOAP response:
<?xml version="1.0" encoding="utf-8" ?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<GetCurrentExchangeRatesResponse xmlns="http://www.mnb.hu/webservices/">
<GetCurrentExchangeRatesResult>
<MNBCurrentExchangeRates>
<Day date="2013-12-03">
<Rate curr="AUD" unit="1">203,5</Rate>
<Rate curr="BGN" unit="1">154,82</Rate>
<Rate curr="BRL" unit="1">94,69</Rate>
<Rate curr="CAD" unit="1">209,79</Rate>
<Rate curr="CHF" unit="1">246,21</Rate>
<Rate curr="CNY" unit="1">36,62</Rate>
<Rate curr="CZK" unit="1">11,02</Rate>
<Rate curr="DKK" unit="1">40,59</Rate>
<Rate curr="EUR" unit="1">302,8</Rate>
<Rate curr="GBP" unit="1">366,05</Rate>
<Rate curr="HKD" unit="1">28,78</Rate>
<Rate curr="HRK" unit="1">39,67</Rate>
<Rate curr="IDR" unit="100">1,88</Rate>
<Rate curr="ILS" unit="1">63,32</Rate>
<Rate curr="INR" unit="1">3,58</Rate>
<Rate curr="ISK" unit="1">1,87</Rate>
<Rate curr="JPY" unit="100">217,08</Rate>
<Rate curr="KRW" unit="100">21,04</Rate>
<Rate curr="LTL" unit="1">87,71</Rate>
<Rate curr="LVL" unit="1">430,68</Rate>
<Rate curr="MXN" unit="1">16,89</Rate>
<Rate curr="MYR" unit="1">69,33</Rate>
<Rate curr="NOK" unit="1">36,46</Rate>
<Rate curr="NZD" unit="1">183,65</Rate>
<Rate curr="PHP" unit="1">5,1</Rate>
<Rate curr="PLN" unit="1">72,03</Rate>
<Rate curr="RON" unit="1">67,89</Rate>
<Rate curr="RSD" unit="1">2,65</Rate>
<Rate curr="RUB" unit="1">6,71</Rate>
<Rate curr="SEK" unit="1">34,08</Rate>
<Rate curr="SGD" unit="1">177,85</Rate>
<Rate curr="THB" unit="1">6,93</Rate>
<Rate curr="TRY" unit="1">109,6</Rate>
<Rate curr="UAH" unit="1">27,14</Rate>
<Rate curr="USD" unit="1">223,09</Rate>
<Rate curr="ZAR" unit="1">21,59</Rate>
</Day>
</MNBCurrentExchangeRates>
</GetCurrentExchangeRatesResult>
</GetCurrentExchangeRatesResponse>
</soap:Body>
</soap:Envelope>
My XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:ws="http://www.mnb.hu/webservices/">
<xsl:output method="xml" version="1.0" encoding="UTF-8" />
<xsl:template match="/">
<xsl:element name="list">
<xsl:value-of select="soap:Envelope/soap:Body/ws:GetCurrentExchangeRatesResponse/ws:GetCurrentExchangeRatesResult" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
This XSLT works as excpected, but I can´t get any deeper than that. Every time I am trying to get to the MNBCurrentExchangeRates element, select result is empty. (I tryied ws:MNBCurrentExchangeRates, *[local-name()='MNBCurrentExchangeRates'] and many other less realistic options)
If it helps, spec for this soap request states that content of GetCurrentExchangeRatesResult element is string:
HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
Content-Length: length
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<GetCurrentExchangeRatesResponse xmlns="http://www.mnb.hu/webservices/">
<GetCurrentExchangeRatesResult>string</GetCurrentExchangeRatesResult>
</GetCurrentExchangeRatesResponse>
</soap:Body>
</soap:Envelope>
Is there a way to get Day and Rate elements and select them?
Thanks, kwitee
EDIT: I tried both user3016153's and Mathias Müller's answers and none of them worked for me so let me change the question:
Why this XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:ws="http://www.mnb.hu/webservices/">
<xsl:output method="xml" version="1.0" encoding="UTF-8" />
<xsl:template match="/">
<xsl:element name="list">
<xsl:value-of select="//ws:GetCurrentExchangeRatesResult" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
is working and this:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:ws="http://www.mnb.hu/webservices/">
<xsl:output method="xml" version="1.0" encoding="UTF-8" />
<xsl:template match="/">
<xsl:element name="list">
<xsl:value-of select="//ws:MNBCurrentExchangeRates" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
is not?
Mathias Müller's answer created this result (xml encoding is somehow broken and date acces doesn't work):
<?xml version="1.0" encoding="utf-16"?><MNBCurrentExchangeRates><Day date="2013-12-03"><Rate curr="AUD" unit="1">203,5</Rate><Rate curr="BGN" unit="1">154,82</Rate><Rate curr="BRL" unit="1">94,69</Rate><Rate curr="CAD" unit="1">209,79</Rate><Rate curr="CHF" unit="1">246,21</Rate><Rate curr="CNY" unit="1">36,62</Rate><Rate curr="CZK" unit="1">11,02</Rate><Rate curr="DKK" unit="1">40,59</Rate><Rate curr="EUR" unit="1">302,8</Rate><Rate curr="GBP" unit="1">366,05</Rate><Rate curr="HKD" unit="1">28,78</Rate><Rate curr="HRK" unit="1">39,67</Rate><Rate curr="IDR" unit="100">1,88</Rate><Rate curr="ILS" unit="1">63,32</Rate><Rate curr="INR" unit="1">3,58</Rate><Rate curr="ISK" unit="1">1,87</Rate><Rate curr="JPY" unit="100">217,08</Rate><Rate curr="KRW" unit="100">21,04</Rate><Rate curr="LTL" unit="1">87,71</Rate><Rate curr="LVL" unit="1">430,68</Rate><Rate curr="MXN" unit="1">16,89</Rate><Rate curr="MYR" unit="1">69,33</Rate><Rate curr="NOK" unit="1">36,46</Rate><Rate curr="NZD" unit="1">183,65</Rate><Rate curr="PHP" unit="1">5,1</Rate><Rate curr="PLN" unit="1">72,03</Rate><Rate curr="RON" unit="1">67,89</Rate><Rate curr="RSD" unit="1">2,65</Rate><Rate curr="RUB" unit="1">6,71</Rate><Rate curr="SEK" unit="1">34,08</Rate><Rate curr="SGD" unit="1">177,85</Rate><Rate curr="THB" unit="1">6,93</Rate><Rate curr="TRY" unit="1">109,6</Rate><Rate curr="UAH" unit="1">27,14</Rate><Rate curr="USD" unit="1">223,09</Rate><Rate curr="ZAR" unit="1">21,59</Rate></Day></MNBCurrentExchangeRates>
I am pasting .NET code to complete the question:
XslCompiledTransform xslt = new XslCompiledTransform();
using (StringWriter writer = new StringWriter())
{
xslt.Load(xsltPath);
xslt.Transform(xmlDoc.CreateNavigator(), null, writer);
string xml = writer.ToString();
XmlSerializer serializer = new XmlSerializer(typeof(List));
using (TextReader reader = new StringReader(xml))
{
return (List)serializer.Deserialize(reader);
}
}
Upvotes: 1
Views: 750
Reputation: 163
Sorry guys, I misled you. The thing is that the SOAP response is partially escaped:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<GetCurrentExchangeRatesResponse xmlns="http://www.mnb.hu/webservices/">
<GetCurrentExchangeRatesResult><MNBCurrentExchangeRates><Day date="2013-12-04"><Rate curr="AUD" unit="1">200,46</Rate><Rate curr="BGN" unit="1">154,44</Rate><Rate curr="BRL" unit="1">93,56</Rate><Rate curr="CAD" unit="1">208,25</Rate><Rate curr="CHF" unit="1">245,79</Rate><Rate curr="CNY" unit="1">36,49</Rate><Rate curr="CZK" unit="1">11,02</Rate><Rate curr="DKK" unit="1">40,49</Rate><Rate curr="EUR" unit="1">302,05</Rate><Rate curr="GBP" unit="1">363,71</Rate><Rate curr="HKD" unit="1">28,67</Rate><Rate curr="HRK" unit="1">39,53</Rate><Rate curr="IDR" unit="100">1,85</Rate><Rate curr="ILS" unit="1">63,13</Rate><Rate curr="INR" unit="1">3,57</Rate><Rate curr="ISK" unit="1">1,86</Rate><Rate curr="JPY" unit="100">216,66</Rate><Rate curr="KRW" unit="100">20,94</Rate><Rate curr="LTL" unit="1">87,5</Rate><Rate curr="LVL" unit="1">429,71</Rate><Rate curr="MXN" unit="1">16,89</Rate><Rate curr="MYR" unit="1">68,9</Rate><Rate curr="NOK" unit="1">36,35</Rate><Rate curr="NZD" unit="1">181,92</Rate><Rate curr="PHP" unit="1">5,07</Rate><Rate curr="PLN" unit="1">71,9</Rate><Rate curr="RON" unit="1">67,62</Rate><Rate curr="RSD" unit="1">2,63</Rate><Rate curr="RUB" unit="1">6,68</Rate><Rate curr="SEK" unit="1">34,08</Rate><Rate curr="SGD" unit="1">177,05</Rate><Rate curr="THB" unit="1">6,89</Rate><Rate curr="TRY" unit="1">108,51</Rate><Rate curr="UAH" unit="1">26,96</Rate><Rate curr="USD" unit="1">222,29</Rate><Rate curr="ZAR" unit="1">21,4</Rate></Day></MNBCurrentExchangeRates></GetCurrentExchangeRatesResult>
</GetCurrentExchangeRatesResponse>
</soap:Body>
</soap:Envelope>
I exported the XML from Visual Studio and it automaticaly replaced < and >. But the web service is really treating contents of GetCurrentExchangeRatesResult as string.
I spend almost whole day doing this.
Thanks everyone who helped me solve this problem.
Final XSLT that worked for me - require exsl:node-set function (in case someone would need it):
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:exsl="http://exslt.org/common" extension-element-prefixes="exsl" xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:ws="http://www.mnb.hu/webservices/">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="soap:Envelope/soap:Body/ws:GetCurrentExchangeRatesResponse/ws:GetCurrentExchangeRatesResult">
<xsl:element name="kurzovni_listek">
<xsl:variable name="details" select="."/>
<xsl:variable name="parsedXml_">
<xsl:call-template name="parseXml">
<xsl:with-param name="text" select="$details"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="parsedXml" select="exsl:node-set($parsedXml_)"/>
<xsl:attribute name="banka"><xsl:text>MNB</xsl:text></xsl:attribute>
<xsl:attribute name="datum">
<xsl:value-of select="$parsedXml/MNBCurrentExchangeRates/Day/@date"/>
</xsl:attribute>
<xsl:for-each select="$parsedXml/MNBCurrentExchangeRates/Day/Rate">
<xsl:element name="kurz">
<xsl:attribute name="mena">
<xsl:value-of select="@curr"/>
</xsl:attribute>
<xsl:attribute name="nasobek">
<xsl:value-of select="@unit"/>
</xsl:attribute>
<xsl:attribute name="hodnota">
<xsl:value-of select="translate(., ',', '.')"/>
</xsl:attribute>
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:template>
<xsl:template name="parseXml">
<xsl:param name="text"/>
<xsl:choose>
<xsl:when test="contains($text, '>')">
<xsl:variable name="topLevelTag">
<xsl:call-template name="getTopLevelTag">
<xsl:with-param name="text" select="$text"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="openingTag">
<xsl:value-of select="$topLevelTag"/>
</xsl:variable>
<xsl:variable name="tagName">
<xsl:call-template name="getTopLevelTagName">
<xsl:with-param name="text" select="$text"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="closingTag">
<xsl:value-of select="concat('</',$tagName,'>')"/>
</xsl:variable>
<xsl:variable name="firstNode">
<xsl:if test="not(contains($topLevelTag,'/>'))">
<xsl:value-of select="substring-before(substring-after($text,$openingTag),$closingTag)"/>
</xsl:if>
</xsl:variable>
<xsl:variable name="afterFirstNode">
<xsl:choose>
<xsl:when test="not(contains($topLevelTag,'/>'))">
<xsl:value-of select="substring-after($text,concat($firstNode,$closingTag))"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring-after($text,$topLevelTag)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:element name="{$tagName}">
<xsl:call-template name="createAttributes">
<xsl:with-param name="text" select="$topLevelTag"/>
</xsl:call-template>
<xsl:call-template name="parseXml">
<xsl:with-param name="text" select="$firstNode"/>
</xsl:call-template>
</xsl:element>
<xsl:call-template name="parseXml">
<xsl:with-param name="text" select="$afterFirstNode"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="getTopLevelTagName">
<xsl:param name="text"/>
<xsl:choose>
<xsl:when test="contains($text, '>')">
<xsl:variable name="tagWithAttributesWithoutEnd">
<xsl:value-of select="substring-before($text, '>')"/>
</xsl:variable>
<xsl:variable name="tagWithAttributesWithoutBegining">
<xsl:value-of select="substring-after($tagWithAttributesWithoutEnd, '<')"/>
</xsl:variable>
<xsl:variable name="tagName">
<xsl:choose>
<xsl:when test="contains($tagWithAttributesWithoutBegining,' ')">
<xsl:value-of select="substring-before($tagWithAttributesWithoutBegining, ' ')"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$tagWithAttributesWithoutBegining"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:value-of select="$tagName"/>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template name="getTopLevelTag">
<xsl:param name="text"/>
<xsl:choose>
<xsl:when test="contains($text, '>')">
<xsl:variable name="tagWithAttributesWithoutEnd">
<xsl:value-of select="substring-before($text, '>')"/>
</xsl:variable>
<xsl:value-of select="concat($tagWithAttributesWithoutEnd,'>')"/>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template name="createAttributes">
<xsl:param name="text"/>
<xsl:choose>
<xsl:when test="contains($text, '="')">
<xsl:variable name="attributeName">
<xsl:value-of select="substring-before(substring-after($text,' '),'="')"/>
</xsl:variable>
<xsl:variable name="attributeValue">
<xsl:value-of select="substring-before(substring-after($text,concat($attributeName,'="')),'"')"/>
</xsl:variable>
<xsl:attribute name="{$attributeName}"><xsl:value-of select="$attributeValue"/></xsl:attribute>
<xsl:call-template name="createAttributes">
<xsl:with-param name="text" select="substring-after($text,concat($attributeName,'="',$attributeValue,'"'))"/>
</xsl:call-template>
</xsl:when>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Upvotes: 0
Reputation: 116958
In response to your new question: both stylesheets work in the same way and produce a result in the form of:
<?xml version="1.0" encoding="UTF-8"?>
<list xmlns:ws="http://www.mnb.hu/webservices/" xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
203,5
154,82
94,69
209,79
...
223,09
21,59
</list>
The only difference between them is the number of blank lines surrounding the values (either one or two). I am not sure how this helps. I am not a .NET programmer, so I am afraid I can't help you there.
Upvotes: 1
Reputation: 116958
You are missing a for-each statement. Try something like:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
xmlns:ws="http://www.mnb.hu/webservices/"
exclude-result-prefixes="soap ws">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<list>
<xsl:for-each select="soap:Envelope/soap:Body/ws:GetCurrentExchangeRatesResponse/ws:GetCurrentExchangeRatesResult/ws:MNBCurrentExchangeRates/ws:Day/ws:Rate">
<rate>
<date><xsl:value-of select="../@date"/></date>
<curr><xsl:value-of select="@curr"/></curr>
<value><xsl:value-of select="."/></value>
</rate>
</xsl:for-each>
</list>
</xsl:template>
</xsl:stylesheet>
which will produce a result of:
<?xml version="1.0" encoding="UTF-8"?>
<list>
<rate>
<date>2013-12-03</date>
<curr>AUD</curr>
<value>203,5</value>
</rate>
<rate>
<date>2013-12-03</date>
<curr>BGN</curr>
<value>154,82</value>
</rate>
<rate>
<date>2013-12-03</date>
<curr>BRL</curr>
<value>94,69</value>
</rate>
...
<rate>
<date>2013-12-03</date>
<curr>USD</curr>
<value>223,09</value>
</rate>
<rate>
<date>2013-12-03</date>
<curr>ZAR</curr>
<value>21,59</value>
</rate>
</list>
Upvotes: 1
Reputation: 22617
Here is a way to access the Day
and Rate
elements. It processes all Day
elements in an xsl:for-each
expression and matches Rate
elements in a separate template.
Also note that this line:
<xsl:template match="//ws:MNBCurrentExchangeRates">
selects MNBCurrentExchangeRates
elements no matter where they are in the document. That way, you are spared the exercise of navigating this lengthy tree. (If your goal is indeed to get at the Day and Rate elements).
The stylesheet:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
xmlns:ws="http://www.mnb.hu/webservices/"
exclude-result-prefixes="ws">
<xsl:output method="xml" indent="yes" version="1.0" encoding="UTF-8" />
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="//ws:MNBCurrentExchangeRates">
<xsl:element name="list">
<xsl:for-each select="ws:Day">
<xsl:copy>
<xsl:attribute name="date">
<xsl:value-of select="@date"/>
</xsl:attribute>
<xsl:apply-templates/>
</xsl:copy>
</xsl:for-each>
</xsl:element>
</xsl:template>
<xsl:template match="ws:Rate">
<xsl:copy>
<xsl:copy-of select="@*|text()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
This yields the following output:
<?xml version="1.0" encoding="UTF-8"?>
<list>
<Day xmlns="http://www.mnb.hu/webservices/"
xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
date="2013-12-03">
<Rate curr="AUD" unit="1">203,5</Rate>
<Rate curr="BGN" unit="1">154,82</Rate>
<Rate curr="BRL" unit="1">94,69</Rate>
<Rate curr="CAD" unit="1">209,79</Rate>
<Rate curr="CHF" unit="1">246,21</Rate>
<Rate curr="CNY" unit="1">36,62</Rate>
<Rate curr="CZK" unit="1">11,02</Rate>
<Rate curr="DKK" unit="1">40,59</Rate>
<Rate curr="EUR" unit="1">302,8</Rate>
<Rate curr="GBP" unit="1">366,05</Rate>
<Rate curr="HKD" unit="1">28,78</Rate>
<Rate curr="HRK" unit="1">39,67</Rate>
<Rate curr="IDR" unit="100">1,88</Rate>
<!--And so on...-->
</Day>
</list>
Upvotes: 1