How to generate Objective C class files from XML schema? - iphone

I have an XML schema that defines my data model. I would now like to have Objective C source files generated from the XML schema. Does anyone know how to accomplish this?

Take a look at this Stack Overflow question on XML serialization, which mentions a project along these lines.

Without knowing the details my immediate thought would be to probably use xslt for this. e.g. if you had something like (I appreciate
<element name="SomeEntity">
<attribute name="someAttr" type="integer" />
<complexType>
<sequence>
<element name="someOtherAttr" type="string" />
</sequence>
</complexType>
</entity>
Create a bunch of templates to translate this,e.g.
<xsl:template match="element">
<xsl:apply-template select="." mode="header"/>
<xsl:apply-template select="." mode="impl"/>
</xsl:template>
<xsl:template match="element" mode="header">
class <xsl:value-of select="#name"/> {
public:
<xsl:apply-template select="attribute" mode="header"/>
<xsl:apply-template select="complexType/element" mode="header"/>
</xsl:template>
...
Though if the logic on the generation is more complex I would probably go down the road of importing the xml into an object model and programmatically process that, possibly using a template engine such as Velocity, as while it is possible complex logic in xslt is a pain.

Related

Burst Mode Vs Fully Streaming XSLT

I have written an XSLT to transform a huge incoming XML file to JSON using burst mode streaming. I am new to XSLT and have heard that there is a better way of fully streaming XSLT code which is more efficient and faster then burst mode.
Can someone please help me understand -
1. What is the difference between burst mode vs Fully streaming ?
2. How can i convert below XSLT code to fully streaming to improve the perfomance?
Below is my burst mode XSLT code -
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:wd="urn:com.workday.report/INT1109_CR_REV_Customer_Invoices_to_Connect" exclude-result-prefixes="xs" version="3.0">
<xsl:mode streamable="yes" on-no-match="shallow-skip"/>
<xsl:output method="text" encoding="UTF-8" indent="no"/>
<xsl:template match="wd:Report_Data">
<xsl:iterate select="wd:Report_Entry/copy-of()">
<!--Define Running Totals for Statistics -->
<xsl:param name="TotalHeaderCount" select="0"/>
<xsl:param name="TotalLinesCount" select="0"/>
<!--Write Statistics -->
<xsl:on-completion>
<xsl:text>{"Stats": </xsl:text>
<xsl:text>{"Total Header Count": </xsl:text>
<xsl:value-of select="$TotalHeaderCount"/>
<xsl:text>,</xsl:text>
<xsl:text>"Total Lines Count": </xsl:text>
<xsl:value-of select="$TotalLinesCount"/>
<xsl:text>}}</xsl:text>
</xsl:on-completion>
<!--Write Header Details -->
<xsl:text>{"id": "</xsl:text>
<xsl:value-of select="wd:id"/>
<xsl:text>",</xsl:text>
<xsl:text>"revenue_stream": "</xsl:text>
<xsl:value-of select="wd:revenue_stream"/>
<xsl:text>",</xsl:text>
<!--Write Line Details -->
<xsl:text>"lines": [ </xsl:text>
<!-- Count the number of lines for an invoice -->
<xsl:variable name="Linescount" select="wd:total_lines"/>
<xsl:iterate select="wd:lines">
<xsl:text> {</xsl:text>
<xsl:text>"sequence": </xsl:text>
<xsl:value-of select="wd:sequence"/>
<xsl:text>,</xsl:text>
<xsl:text>"sales_item_id": "</xsl:text>
<xsl:value-of select="wd:sales_item_id"/>
<xsl:text>",</xsl:text>
</xsl:iterate>
<xsl:text>}]}
</xsl:text>
<!--Store Running Totals -->
<xsl:next-iteration>
<xsl:with-param name="TotalHeaderCount" select="$TotalHeaderCount + 1"/>
<xsl:with-param name="TotalLinesCount" select="$TotalLinesCount + $Linescount"/>
</xsl:next-iteration>
</xsl:iterate>
</xsl:template>
</xsl:stylesheet>
Here is the sample XML -
<?xml version="1.0" encoding="UTF-8"?>
<wd:Report_Data xmlns:wd="urn:com.workday.report/INT1109_CR_REV_Customer_Invoices_to_Connect">
<wd:Report_Entry>
<wd:id>CUSTOMER_INVOICE-6-1</wd:id>
<wd:revenue_stream>TESTA</wd:revenue_stream>
<wd:total_lines>1</wd:total_lines>
<wd:lines>
<wd:sequence>ab</wd:sequence>
<wd:sales_item_id>Administrative Cost</wd:sales_item_id>
</wd:lines>
</wd:Report_Entry>
<wd:Report_Entry>
<wd:id>CUSTOMER_INVOICE-6-10</wd:id>
<wd:revenue_stream>TESTB</wd:revenue_stream>
<wd:total_lines>1</wd:total_lines>
<wd:lines>
<wd:sequence>ab</wd:sequence>
<wd:sales_item_id>Data - Web Access</wd:sales_item_id>
</wd:lines>
</wd:Report_Entry>
</wd:Report_Data>
If the order of properties in the JSON doesn't matter then you could directly create XSLT/XPath 3 maps and arrays with xsl:map/xsl:map-entry (or the XPath 3.1 map constructor) and the Saxon specific extension element saxon:array (unfortunately the XSLT 3 language standard lacks an instruction to create an array). Furthermore most of your iteration parameters seem to be easily implemented as accumulators:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:saxon="http://saxon.sf.net/"
extension-element-prefixes="saxon"
xpath-default-namespace="urn:com.workday.report/INT1109_CR_REV_Customer_Invoices_to_Connect"
exclude-result-prefixes="#all"
version="3.0">
<xsl:output method="adaptive" indent="yes"/>
<xsl:mode streamable="yes" use-accumulators="#all" on-no-match="shallow-skip"/>
<xsl:accumulator name="header-count" as="xs:integer" initial-value="0" streamable="yes">
<xsl:accumulator-rule match="Report_Entry" select="$value + 1"/>
</xsl:accumulator>
<xsl:accumulator name="lines-count" as="xs:integer" initial-value="0" streamable="yes">
<xsl:accumulator-rule match="Report_Entry/total_lines/text()" select="$value + xs:integer(.)"/>
</xsl:accumulator>
<xsl:template match="Report_Data">
<xsl:apply-templates/>
<xsl:sequence
select="map {
'Stats': map {
'Total Header Count' : accumulator-after('header-count'),
'Total Lines Count' : accumulator-after('lines-count')
}
}"/>
</xsl:template>
<xsl:template match="Report_Entry">
<xsl:map>
<xsl:apply-templates/>
</xsl:map>
</xsl:template>
<xsl:template match="Report_Entry/id | Report_Entry/revenue_stream | lines/sequence | lines/sales_item_id">
<xsl:map-entry key="local-name()" select="string()"/>
</xsl:template>
<xsl:template match="Report_Entry/lines">
<xsl:map-entry key="local-name()">
<saxon:array>
<xsl:apply-templates/>
</saxon:array>
</xsl:map-entry>
</xsl:template>
</xsl:stylesheet>
The example uses output method adaptive as your current sample doesn't create a single JSON object and I have simply tried to create the same output as your current code; the JSON output method would need a single map or array as the main sequence result.
Code works with streaming and Saxon EE 9.9.1.1 in oXygen, unfortunately 9.8 doesn't consider the code streamable.
As for general rules, there are limits as to what you can achieve with accumulators and template matching when streaming; as you can see, the accumulator to sum up the values from the total_lines elements needs to match on the text child to not consume the element in the accumulator (Saxon has another extension of capturing accumulators to ease such tasks however).
So far I would rather say it is more important to find a way to get around the streamability analysis and to have the streamable code return the right and same result as the non-streamable code; for instance, while experimenting with an approach to generate JSON with streaming using two transformation steps where some sample data similar to yours is the input, the XML representation for JSON the result of the first transformation and the JSON supposed to be the result of using xml-to-json on the first step's result I ran into a Saxon bug https://saxonica.plan.io/issues/4215.
With streaming, it seems there is not enough test coverage or implementation maturity to be able to combine features reliably in a complex and scalable way, partly due to a complex spec, partly due to the limited use of that stuff by the XSLT community.
So if you find a working way for a particular problem to use streaming to keep memory consumption lower or manageable compared to the normal XSLT 2/3 tree based processing then you can of course experiment with changes to improve performance but it is easy to break things.
One general observation is that streaming allows you to access all attributes of the currently processed/matched element but not its children, therefore it can help immensely to insert a processing steps that transforms elements into attributes if you have a simple child element structure. That way you can then often avoid any copy-of(). But of course you need a way to combine two stylesheets which Saxon allows with its API but doing it requires writing Java or .NET code.

Creating a comment that gets displayed in generated request? WSDL SOAP xml

Im in the process of writing a wsdl file for an existing system. I'd like to add comments to generated requests.
For instance this:
<xsd:simpleType name="coffeetype">
<xsd:restriction base="xsd:integer">
<!--0=likescoffee,1=doesnotlikecoffe-->
<xsd:enumeration value="0" />
<xsd:enumeration value="1" />
</xsd:restriction>
</xsd:simpleType>
<xsd:element name="CoffeeRequestInput" nillable="false" type="tns:coffeetype" />
Should look like this in the generated request: (eg. when loading the WSDL in SoapUI)
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsdl="https://example.com/some.wsdl">
<soapenv:Header/>
<soapenv:Body>
<!--0=likescoffee,1=doesnotlikecoffe-->
<wsdl:CoffeeRequestInput>0</wsdl:CoffeeRequestInput>
</soapenv:Body>
</soapenv:Envelope>
I was able to see these comments when opening the WSDL but not when generating a request from that WSDL.
Already looked into annotations but I wasn't able to use them to create the result I wanted. (Probably an error on my side)
In short you cannot create documentation in requests as you would like to. However you can generate documentation from your WSDL that can be be very useful. By using the "xsd:documentation" tag you can add documentation directly to the elements.
For example
<xsd:simpleType name="coffeetype">
<xsd:restriction base="xsd:integer">
<xsd:enumeration value="0" />
<xsd:enumeration value="1" />
</xsd:restriction>
</xsd:simpleType>
<xsd:element name="CoffeeRequestInput" nillable="false" type="tns:coffeetype">
<xsd:annotation>
<xsd:documentation>
This object is the CoffeeRequestInput object is an Enumeration which can be used to determine is the user sending the request likes coffee or not.
Valid values for the enumeration is as follows:
0 = like coffee
1 = does not like coffee (probably a user not a programmer making a request).
Some other things that you need to document goes here.
</xsd:documentation>
</xsd:annotation>
</xsd:element>
You can then use software such as Altova StyleForce, LiquidXML and OxyGen to generate PDF, Word documents or HTML pages which shows the SOAP services and operations with all your comments included.
If you feel up to it you can write your own XLST to transform your WSDL and XSD's into a neat HTML page which documents you interfaces as well. The best part about this is that when you update the WSDL with new operations and so on that the documentation is updated as well.

Salesforce Apex Error 'Apex type not found for element products'

I've generated Apex from a WSDL, but when invoking a callout I get the error 'Unable to parse callout response. Apex type not found for element products'. I know it has something to do when I generated the WSDL, but not sure how to fix it or why. Here are the pieces that I believe is causing the problem. I know there are other solutions and I have looked at those, but this seems different because of the type="impl:..."
<complexType name="myVData">
<sequence>
<element name="products" nillable="true" type="impl:ArrayOf_tns1_VTP"/>
</sequence>
...
<complexType name="ArrayOf_tns1_VTP">
<sequence>
<element maxOccurs="unbounded" minOccurs="0" name="item" type="tns1:VTP"/>
</sequence>
</complexType>
Anyone got a clue? Thanks!
Found the error if anyone else runs into the same problem. First I found that SOAPUI is a very helpful tool for figuring out what the response should be like.
Second, the generated Apex class from the wsdl was wrong (or wsdl, however you want to look at it) and the apex was looking for products and not item (as shown in the ArrayOf_tns1_VTP complex. I changed the name="item" to name="products" in the wsdl, regenerated the Apex and works now!

How to add element in XSD Schema that takes comma separated String values

I am new in XML schema ...
I have a xsd schema file :-
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.anirban.com/v1"
xmlns:tns="http://www.anirban.com/v1" elementFormDefault="qualified">
<complexType name="InputRequest">
<sequence>
<element name="Name" maxOccurs="unbounded" type="string">
</element>
<element name="Url" type="anyURI" maxOccurs="1" minOccurs="0">
</element>
<element name="Employee">
<complexType>
<sequence>
<element name="VehicleList" maxOccurs="unbounded" type="string" />
<element name="EmployeeName" type="string" default="Anirban" />
</sequence>
</complexType>
</element>
</sequence>
</complexType>
<complexType name="OutputResponse">
<sequence>
<element name="ResponseCode" type="string"></element>
</sequence>
</complexType>
<element name="getInputRequest" type="tns:InputRequest"></element>
<element name="getOutputResponse" type="tns:OutputResponse"></element>
</schema>
Which is used to create a SOAP Request like :-
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:v1="http://www.anirban.com/v1">
<soapenv:Header/>
<soapenv:Body>
<v1:getInputRequest>
<!--1 or more repetitions:-->
<v1:Name>Abc</v1:Name>
<v1:Name>Efg</v1:Name>
<!--Optional:-->
<v1:Url>http://localhost:8084/test?wsdl</v1:Url>
<v1:Employee>
<!--1 or more repetitions:-->
<v1:VehicleList>ee</v1:VehicleList>
<v1:VehicleList>ff</v1:VehicleList>
<v1:EmployeeName>Anirban</v1:EmployeeName>
</v1:Employee>
</v1:getInputRequest>
</soapenv:Body>
</soapenv:Envelope>
Now my question is, I want to add an element in between like :- <v1:Orders>Ord1,Ord2,Ord3</v1:Orders> which will take String value with comma separated .. So how can I add the the element Orders in between ? Is it possible to add an element which will take comma separated String values in the sequence of complexType ??? Please help ...
As helderdarocha has already pointed out, you would almost certainly do better to specify your element as a sequence of elements:
<v1:Order>Ord1</v1:Order>
<v1:Order>Ord2</v1:Order>
<v1:Order>Ord3</v1:Order>
You can wrap them in a v1:Orders element if you like; different people have different tastes when it comes to things like that.
If for some reason it really is better to make a single element with a sequence of atomic values (I don't actually believe for a moment that it really will be better, but many people need to learn by painful experience of their own instead of learning from other people's painful experience), then it will work better in XSD and related tools to make the list white-space separated:
<v1:Orders>Ord1 Ord2 Ord3</v1:Orders>
That way, the Orders element can be declared as a list of simple values, and the individual values can be tested against a type (integer, date, IDREF, NCName, regex-defined token of your choice, ...). The XSD validator, and schema-aware software downstream, will see a sequence of simple values, not one.
Making it a comma-delimited list is, from an XSD point of view, the worst of all possible solutions. XSD list types are whitespace-delimited, not comma-delimited (and not arbitrary-regex-delimited, no matter how much the Perl aficionados of the world might have liked that), so neither the XSD validator nor downstream schema-aware applications will see a sequence of values; they'll see just one value, with a lexical space constrained by a long complicated (and possibly faulty, unless you're better than the average programmer) regular expression. If the individual tokens are supposed to represent integers, dates, or some other XSD simple type, you get no type validation for them. If you want to restrict the length of the list, it's possible only by means of the regex used to validate the string; another opportunity for error.
If on the other hand the individual tokens have no lexical constraints, and you really really insist on having a comma-delimited list and not a whitespace-delimited list, you have no work to do at all: just declare the Orders element as xsd:string. Since XSD won't do anything special with the commas, there is no need for a pattern that does anything clever with commas; since the tokens have no lexical constraints, there is no work for a pattern to do in any case.

Microsoft Dynamics CRM 2011 PlugIn RetrieveMultiple Does not return custom attribute on pricelist

I am writing a simple PlugIn for Dynamics CRM 2011 which should retrieve a custom attribute which I customized in the pricelist entity.
When I execute the following code, all standard attributes are returned, but not the one custom attribute which I customized (and published of course). I don't get an error, but simply the attribute 'axi_submarketid' is not returned. If I read data from a completely custom entity, I have no problem.
string fetch = string.Format(#"<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'>
<entity name='pricelevel'>
<attribute name='name' />
<attribute name='transactioncurrencyid' />
<attribute name='enddate' />
<attribute name='begindate' />
<attribute name='statecode' />
<attribute name='pricelevelid' />
<attribute name='axi_submarketid' />
<order attribute='name' descending='false' />
<filter type='and'>
<condition attribute='pricelevelid' operator='eq' uiname='10 Stromlieferung' uitype='pricelevel' value='{0}' />
</filter>
</entity>
</fetch>", pricelevelid);
EntityCollection pricelevel = _service.RetrieveMultiple(new FetchExpression(fetch));
What am I doing wrong?
Thanks for help,
Peter
I solved my issue myself and it is a bit embarassing :-)
The expected attribute was not deliverd, because it had no content. Meaning, not only in the direct context of a PlugIn, but also when requesting data in general: if in an attribute there is no data, the attribute is not sent back, for example if the attribute is NULL or has no value in it.
Thanks anyway, hope this question and answer helps someone else.
Regards,
Peter