This past week while working on a module in Mendix that required the consumption of a web service, I ran into the situation where the response was CDATA. I hadn’t encountered that before so I did a bit of research. Apparently this is used to extend and change API’s without changing the structure of the WSDL. This makes a lot of sense since once an API is created there can be 1 to n number of other systems using it and any change will require all of the end users to change their work. If you’ve ever encountered this or want to understand how to deal with parsing CDATA in Mendix, read on!
I decided to create a sample project so I could share the solution. I searched the web for a public WSDL library and found a Stock Quote WSDL that returned CDATA. This is a simple WSDL that asks for the stock symbol and will return a string enclosed in the CDATA wrapper containing an XML format. The string returned looks like this when I entered ‘AMZN’ (Amazon):
<StockQuotes><Stock><Symbol>amzn</Symbol><Last>548.43</Last><Date>10/14/2015</Date><Time>1:09pm</Time><Change>-0.47</Change><Open>551.30</Open><High>552.25</High><Low>539.68</Low><Volume>2073357</Volume><MktCap>256.51B</MktCap><PreviousClose>548.90</PreviousClose><PercentageChange>-0.09%</PercentageChange><AnnRange>284.00 - 580.57</AnnRange><Earns>-0.41</Earns><P-E>N/A</P-E><Name>Amazon.com</Name></Stock></StockQuotes>
As you can see, all of the attributes are returned in an XML format; however, the string is the only thing returned and must be dealt with.
I started off asking one of our Java developers to create a Java Action that would parse the string, but we quickly realized the error because the parser would need to be able to deal with more complex layouts that contain arrays or in a normalized structure. I reached out to one of my developer friends at Mendix, Eric Tieniber, and he quickly had a much better solution. Since I learned something in the process, I wanted to capture it in this blog so we can all learn together (and I keep this as a reference in the future!) Here was his response:
1. Consume the web service as it was defined – theoretically you should be able to capture the CDATA as a string.
2. Once you have the CDATA string, strip off any extraneous “CDATA” tags on it and get it into a clean string of just XML.
3. Create an XML Schema (XSD file) based on your CDATA XML.
a. You can do this easily online here: http://www.freeformatter.com/xsd-generator.html
b. Just paste your sample XML and it will create the schema based on the data there
4. In your domain model, click the “Import web service/XML file” button.
5. Follow the wizard to automatically:
a. Import your newly generated schema that you created above
b. Create non-persistable domain model objects
6. Finally, in a microflow:
a. Convert your string to a file document using CommunityCommons.StringToFile
b. Use the “Import XML” activity to take the file and parse it with the XML->Domain mapping
c. Your output will be the non-persistable entities that you ultimately need
Now let’s see it in action:
First, I took that string and from the CDATA result and parsed it in the website Eric proposed to create an .XSD file.
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="StockQuotes"> <xs:complexType> <xs:sequence> <xs:element name="Stock"> <xs:complexType> <xs:sequence> <xs:element type="xs:string" name="Symbol"/> <xs:element type="xs:float" name="Last"/> <xs:element type="xs:string" name="Date"/> <xs:element type="xs:string" name="Time"/> <xs:element type="xs:float" name="Change"/> <xs:element type="xs:float" name="Open"/> <xs:element type="xs:float" name="High"/> <xs:element type="xs:float" name="Low"/> <xs:element type="xs:int" name="Volume"/> <xs:element type="xs:string" name="MktCap"/> <xs:element type="xs:float" name="PreviousClose"/> <xs:element type="xs:string" name="PercentageChange"/> <xs:element type="xs:string" name="AnnRange"/> <xs:element type="xs:float" name="Earns"/> <xs:element type="xs:string" name="P-E"/> <xs:element type="xs:string" name="Name"/> </xs:sequence> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> </xs:element> </xs:schema>
I selected the “Import Web Service/XML file…” button at the top of my domain and used it to import my .XSD file. I won’t detail that process as the wizard is really straight forward and there is already a lot of help out there in Mendix’s Documentation to detail how to use the wizard. Once it finished, it created two entities: StockQuotes and Stock with a 1-1 association between them.
I then created the microflow to fetch the information using these mappings. For the Web service call, I built my own domain-to-XML mapping for the body of the request even though I didn’t need to because it was a simple request. It is good practice and a hard habit to break.
Finally, I setup my page, launched the app, and typed in some stocks to get the latest information. Here is that in action:
I hope this is useful to those out there that need to leverage CDATA construction or deconstruction, or just need to consume information in a pre-defined XML structure. Thanks to Eric for the step-by-step!