.NET SOAP Serializing Unbounded Sequence - c#

We have a WSDL which contains the following type definition:
...
<xsd:complexType name="OrderItem">
<xsd:all>
<xsd:element name="source" type="xsd:string" />
</xsd:all>
</xsd:complexType>
<xsd:complexType name="OrderItems">
<xsd:sequence>
<xsd:element name="item" type="tns:OrderItem" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
...
When adding the service as a Service Reference in VS 2010, the OrderItems class contains an item property which is of type OrderItem[]. The SOAP request is then generated as follows:
...
<items>
<OrderItem>
<item>foo</item>
<item>bar</item>
</OrderItem>
</items>
...
Using the XmlArray and XmlArrayItem attributes we can control the names of the <OrderItem> and <item> elements respectively, but can't get to the desired structure:
...
<items>
<item>foo</item>
<item>bar</item>
</items>
...
I'm aware that this problem could be avoided if the WSDL specified something like <xsd:restriction base="soap-enc:Array"> rather than an unbounded sequence, but given the above is the only way forward to use some custom serialization?
EDIT: Example WSDL at https://gist.github.com/1422704

It seems that .NET WCF services do not play nice with our WSDL (which was manually created with a focus on the XSD and not on SOAP).
The easiest way to get the SOAP API to work with .NET was to alter the WSDL to use the SOAP array type, so <items> becomes a soap-enc:Array with soap-enc:arrayType="tns:OrderItem[]".
The resulting XML generated by the Service Reference's auto-generated code is then correct.

Related

XML Deserialize - Same element name different xsi:type. No need to validate

We extract thousands of XML-files from a source we can't control which have the same name but different xsi:type. We want to deserialize these XML to C# and we are generating this C# class from this XSD with xsd.exe.
I'm not sure if we are able to fix the XSD we generate from or if we need to handle the issue from another perspective (changing the generateted C# class, which is a mess, or add another layer somewhere).
From what I understand with XML 1.0 xsi:type can be used to achieve this wish xsi:type=" another-ns: ExportStatement" but this is not possible since I can't change the XML files.
The only difference between, from what I see, is that ExportStatement has two more elements:
<xsd:element name="Letter" type="xsd:string" minOccurs="0" />
<xsd:element name="No" type="xsd:string" minOccurs="0" />
Like I said, we only use the XSD to generate classes, we don't need to validate XML-files.
Here is a sample of the XML-file:
<Phrase xsi:type="ExportPhrase" Id="114122" DiscriminatorId="292" SortOrder="0" PhraseField="0">
<EuPhracPhraseId xsi:nil="true" />
<AdditionalLocales />
<MergePhrases />
<Texts>
<Text LocaleId="1">
<Text><![CDATA[AData]]></Text>
</Text>
<Text LocaleId="2">
<Text><![CDATA[BData]]></Text>
</Text>
<Text LocaleId="3">
<Text><![CDATA[CData]]></Text>
</Text>
</Texts>
</Phrase>
<Phrase xsi:type="ExportStatement" Id="30010100" DiscriminatorId="1042" SortOrder="0" PhraseField="181">
<EuPhracPhraseId xsi:nil="true" />
<AdditionalLocales />
<MergePhrases />
<Letter>P</Letter>
<No>101</No>
<Texts>
<Text LocaleId="1">
<Text><![CDATA[EData]]></Text>
</Text>
<Text LocaleId="2">
<Text><![CDATA[FData]]></Text>
</Text>
<Text LocaleId="3">
<Text><![CDATA[GData]]></Text>
</Text>
</Texts>
</Phrase>
Part of the XSD:
<xsd:element name="Phrases">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="Phrase" type="ExportStatement" />
<xsd:element name="Phrase" type="ExportPhrase" />
<xsd:element name="ExportStatement" type="ExportStatement" />
</xsd:choice>
</xsd:complexType>
</xsd:element>
XMLSerializer:
private static XMLDoc GetMaster(XmlSerializer xmlDocSerializer)
{
var masterFile = "XSD\\master.xml";
TextReader masterReader = new StreamReader(masterFile, Encoding.Unicode);
var master = (XMLDoc)xmlDocSerializer.Deserialize(masterReader);
return master;

"The element is missing.." trying to generate class with <element ref = />

Using the XSD tool included with VS 2013, I receive the following message trying to generate a class from an xsd that contains <xsd:element ref=.../> -
Schema validation warning: The 'http://www.w3.org/2000/09/xmldsig#:KeyName' element is not declared. Line 14, position 8.
Warning: Schema could not be validated. Class generation may fail or may produce incorrect results.
Error: Error generating classes for schema 'test'.
- The element 'http://www.w3.org/2000/09/xmldsig#:Signature' is missing.
This is a cut down xsd that demonstrates the problem:
<?xml version="1.0" encoding="utf-8"?>
<xsd:schema id="test"
targetNamespace="http://tempuri.org/test.xsd"
elementFormDefault="qualified"
xmlns="http://tempuri.org/test.xsd"
xmlns:mstns="http://tempuri.org/test.xsd"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:sig="http://www.w3.org/2000/09/xmldsig#"
>
<xsd:import schemaLocation="xmldsig-core-schema.xsd" namespace="http://www.w3.org/2000/09/xmldsig#" />
<xsd:complexType name="test" >
<xsd:sequence >
<xsd:element ref="sig:Signature" minOccurs="0" maxOccurs="unbounded"></xsd:element>
</xsd:sequence>
</xsd:complexType>
<xsd:element type="test" name="top"/>
</xsd:schema>
I'm pretty sure the import and namespaces are okay. Resharper and the VS Schema Designer do not complain. I suspect that this is something that the tool just doesn't do.
Any ideas how I can proceed?
It turns out that this has been answered here.
https://stackoverflow.com/a/17278163/2516770
I need to add the imported file to the file list of the xsd command line parameters:
xsd test.xsd xmldsig-core-schema.xsd /c

Why is my XmlReader not validating against the Schema?

I'm trying to read an XML file and validate against the schema specified by that file. I will not know the schema's location ahead of time, so I need to use the schema specified by the xml file.
Here's the relevant code (inspired by this answer):
var settings = new XmlReaderSettings();
settings.ValidationType = ValidationType.Schema;
settings.ValidationFlags |= XmlSchemaValidationFlags.ProcessInlineSchema;
settings.ValidationFlags |= XmlSchemaValidationFlags.ProcessSchemaLocation;
settings.ValidationFlags |= XmlSchemaValidationFlags.ReportValidationWarnings;
settings.ValidationEventHandler += new ValidationEventHandler(ValidationFailed);
//settings.Schemas.Add("http://www.publishing.org", new XmlTextReader(#"C:\path\to\schema\Book.xsd"));
validatingReader = XmlReader.Create(xmlInputReader, settings);
while (validatingReader.Read()) ;
If I uncomment the settings.Schemas.Add line and comment the settings.ValidationFlags |= XmlSchemaValidationFlags.ProcessSchemaLocation out, everything works. I have also tested both the schema and the XML against an external validator.
The event handler message reports "Cannot load the schema for the namespace 'http://www.publishing.org' - Specified argument was out of the range of valid values. Parameter name: baseUri." and it occurs on line 2 (at the root element), followed by "Could not find schema information for the element 'http://www.publishing.org:[each element]'.
My first thought (and still the only thing I know it can be) was that the URI wasn't pointing to the xsd, but I've used 1) A full path via file:///C:\path\to\schema\Book.xsd, 2) A URI relative to the xml file, and 3) A URI relative to the application's current directory. The Visual Studio XML editor has no problem with any of these, but the XmlReader can't seem to find any of them.
Here's a simple schema and an xml instance (my actual schema is more complex, but this fails too):
<?xml version="1.0"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.publishing.org" xmlns="http://www.publishing.org"
version="1.0" elementFormDefault="qualified">
<xsd:element name="Book" type="BookType"/>
<xsd:complexType name="BookType">
<xsd:sequence>
<xsd:element name="Title" type="xsd:string" minOccurs="1" maxOccurs="1"/>
<xsd:element name="Author" type="xsd:string" minOccurs="1" maxOccurs="unbounded"/>
<xsd:element name="Date" type="xsd:string" minOccurs="1" maxOccurs="1"/>
<xsd:element name="ISBN" type="xsd:string" minOccurs="1" maxOccurs="1"/>
<xsd:element name="Publisher" type="xsd:string" minOccurs="1" maxOccurs="1"/>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
<?xml version="1.0" encoding="UTF-8"?>
<Book xmlns="http://www.publishing.org" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.publishing.org ../etc/Book.xsd">
<!-- Book.xsd file:///C:\path\to\schema\Book.xsd -->
<Title>Historic Doubts Relative to Napoleon Bonaparte</Title>
<Author>Richard Whately</Author>
<Author>Whately, Richard</Author>
<Date>1849</Date>
<ISBN>1465554777</ISBN>
<Publisher>Warren P. Draper</Publisher>
</Book>
I think everything is correct concerning my namespaces. I have also tried loading through an XmlDocument, but I get the same results. It has to be a problem locating the XSD, right?
I agree it should be a path problem.
I was able to use your code ( and the example you used :) ) .
I tested the validation against a local copy of the xsd, in a file, by setting my local file path inside the xml.
It did nothing when I used your exact xml, and indeed threw the validation error if I changed a tag.
My xsi:schemaLocation looks like:
xsi:schemaLocation="http://www.publishing.org C:\Users\Mike\Desktop\xml_test_files\test.xsd"
Did you try that simple local folder path?

Contract-First WCF for Salesforce Outbound Messaging

I am looking at implementing listener application for Salesforce Outbound Messaging.
The walk through implements it using the deprecated ASMX web service. The code is generated using wsdl.exe with /serverInterface switch.
Here is the wsdl of Salesforce Outbound Messaging.
<?xml version="1.0" encoding="UTF-8"?>
<definitions targetNamespace="http://soap.sforce.com/2005/09/outbound"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://soap.sforce.com/2005/09/outbound"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:ent="urn:enterprise.soap.sforce.com"
xmlns:ens="urn:sobject.enterprise.soap.sforce.com">
<types>
<schema elementFormDefault="qualified" xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:enterprise.soap.sforce.com">
<!-- Our simple ID Type -->
<simpleType name="ID">
<restriction base="xsd:string">
<length value="18"/>
<pattern value='[a-zA-Z0-9]{18}'/>
</restriction>
</simpleType>
</schema>
<schema elementFormDefault="qualified" xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:sobject.enterprise.soap.sforce.com">
<import namespace="urn:enterprise.soap.sforce.com" />
<!-- Base sObject (abstract) -->
<complexType name="sObject">
<sequence>
<element name="fieldsToNull" type="xsd:string" nillable="true" minOccurs="0" maxOccurs="unbounded"/>
<element name="Id" type="ent:ID" nillable="true" />
</sequence>
</complexType>
<complexType name="AggregateResult">
<complexContent>
<extension base="ens:sObject">
<sequence>
<any namespace="##targetNamespace" minOccurs="0" maxOccurs="unbounded" processContents="lax"/>
</sequence>
</extension>
</complexContent>
</complexType>
<complexType name="Contact">
<complexContent>
<extension base="ens:sObject">
<sequence>
<element name="Email" nillable="true" minOccurs="0" type="xsd:string"/>
<element name="FirstName" nillable="true" minOccurs="0" type="xsd:string"/>
<element name="LastName" nillable="true" minOccurs="0" type="xsd:string"/>
</sequence>
</extension>
</complexContent>
</complexType>
</schema>
<schema elementFormDefault="qualified" xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://soap.sforce.com/2005/09/outbound">
<import namespace="urn:enterprise.soap.sforce.com" />
<import namespace="urn:sobject.enterprise.soap.sforce.com" />
<element name="notifications">
<complexType>
<sequence>
<element name="OrganizationId" type="ent:ID" />
<element name="ActionId" type="ent:ID" />
<element name="SessionId" type="xsd:string" nillable="true" />
<element name="EnterpriseUrl" type="xsd:string" />
<element name="PartnerUrl" type="xsd:string" />
<element name="Notification" maxOccurs="100" type="tns:ContactNotification" />
</sequence>
</complexType>
</element>
<complexType name="ContactNotification">
<sequence>
<element name="Id" type="ent:ID" />
<element name="sObject" type="ens:Contact" />
</sequence>
</complexType>
<element name="notificationsResponse">
<complexType>
<sequence>
<element name="Ack" type="xsd:boolean" />
</sequence>
</complexType>
</element>
</schema>
</types>
<!-- Method Messages -->
<message name="notificationsRequest">
<part element="tns:notifications" name="request"/>
</message>
<message name="notificationsResponse">
<part element="tns:notificationsResponse" name="response"/>
</message>
<!-- PortType -->
<portType name="NotificationPort">
<operation name="notifications">
<documentation>Process a number of notifications.</documentation>
<input message="tns:notificationsRequest"/>
<output message="tns:notificationsResponse"/>
</operation>
</portType>
<!-- Binding
You need to write a service that implements this binding to receive the notifications
-->
<binding name="NotificationBinding" type="tns:NotificationPort">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="notifications">
<soap:operation soapAction=""/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
</operation>
</binding>
<!-- Service Endpoint -->
<service name="NotificationService">
<documentation>Notification Service Implementation</documentation>
<port binding="tns:NotificationBinding" name="Notification">
<soap:address location="http://www.myserver.com/salesforceoutboundprototype/notificationport.svc"/>
</port>
</service>
</definitions>
tldr is I need to implement NotificationBinding so that Salesforce can call my webservice when an event occurs on their system.
I since have realised svcutil does not natively support Contract-First development.
As per Contract-First SOA with WCF I used WSCF.Blue to generate server-side stubs from Salesforce wsdl. Whilst the code compiles wsdl generated by my service does not have the required notifications operation.
I wonder what I am going wrong?
So I managed to do quick implementation of Salesforce wsdl using wsdl.exe and /serverInterface and it seems the wsdl generated by asmx based application is quite different from wcf based application.
This is the interface created by wsdl.exe with /serverInterface
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.1")]
[System.Web.Services.WebServiceBindingAttribute(Name="NotificationBinding", Namespace="http://soap.sforce.com/2005/09/outbound")]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(sObject))]
public interface INotificationBinding {
/// <remarks/>
[System.Web.Services.WebMethodAttribute()]
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Bare)]
[return: System.Xml.Serialization.XmlElementAttribute("notificationsResponse", Namespace="http://soap.sforce.com/2005/09/outbound")]
notificationsResponse notifications([System.Xml.Serialization.XmlElementAttribute("notifications", Namespace="http://soap.sforce.com/2005/09/outbound")] notifications notifications1);
}
This is the interface created by WSCF.Blue
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(Namespace="http://soap.sforce.com/2005/09/outbound", ConfigurationName="INotificationPort")]
public interface INotificationPort
{
// CODEGEN: Generating message contract since the operation notifications is neither RPC nor document wrapped.
[System.ServiceModel.OperationContractAttribute(Action="", ReplyAction="*")]
[System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)]
[System.ServiceModel.ServiceKnownTypeAttribute(typeof(sObject))]
notificationsResponse1 notifications(notificationsRequest request);
}
They seem to be fairly similar so I don't know why wsdl generated by these application would be so different? Is it worthwhile adding wsdls (don't want to make the question any longer then it already is)?
You may not like what I suggest but I believe it will actually be the best option for you. Use ASMX. I have three OM listeners and they all work just fine under 4.0
I do not know who spreads these misfinfromations about some .NET2.0 techs but a lot of them are most certainly not obsoleted just because dev community got stricken with novelty fetish; ASMX is one of them (so is Linq2SQL, etc). WCF is simply put not yet complete, it is not fully WS compliant and that casues a lot of grief with salesforce integration (most painful being that WCF does not support soap headers where salesforce keeps session info).
For more info why asmx is NOT obsolete, see here: Does .net 4.0 still support asmx
Use svcutil to generate a client proxy and simply delete the client proxy implementation, and keep the service/data contracts. The service contract is symmetrical from a client/server point of view. I have worked on substantial contract-first projects and found this to be perfectly adequate.
Just make sure you have all of the external WSDLs available to for svcutil to resolve. I see several import statements in your WSDL text above.
BTW, WSCF.Blue is not quite up to it unfortunately as it's not been updated for a few months and is unlikely to be so in the future. Sadly, the primary developer died in a car crash last year (yes, really.)
I went through the same pain of trying to get my OBM listener working with WCF. I had originally only set up an OperationContract for the notifications method. After deploying I soon realised a Data Contract would need to be set up which I did but due to time constraints and not being entirely sure if that would be all I needed to do I simply switched to an asmx web service which worked.
I figured out an easy way to generate WCF interfaces from the Salesforce WSDL using the built in Visual Studio tools. I downloaded the WSDL from Salesforce and saved it to my desktop. In Visual Studio, go to the add service reference menu in one of your projects(doesn't matter where, you're going to delete this). For the Url, type in the path to the WSDL you downloaded locally (i.e. C:\Users\yourusername\Desktop\notification.wsdl)
This should bring load in the data for the service. Click okay to create the reference and now in the services references folder, double click on the reference you just created. This should show you the object browser with the namespace of the service reference you just created being highlighted. Double click on any of the classes inside this namespace and it will open up the related reference.cs that was generated by visual studio.
Copy all of the content inside the namespace declaration to the file of your choice with whatever namespace you decide. You can rename that interface as well if you plan to handle more than one outbound message (default should be NotificationPort). The interface will have references to the namespace you built it with but you can remove all of the namespacing in the file because all of the classes it is referring to are in the same file.
Create a new WCF service class. Delete the interface that is automatically created with the svc file and change the interface name to the one that you just copied to the new file.
By default, Visual Studio created some an async method which was throwing errors when I tried to implement it. I just deleted it from the interface and used the standard notification method.
I did this in VS2013 but it should be the same for 2010 and 2012 as well. Make sure you are using 'Add Service Reference' and not 'Add Web Reference'. Don't forget to delete the generated reference as you will not be using it.
WSCF provides a contract-first tool with VS integration.

How to make a C# web service accepts a custom object including unbound elements defined in XSD

I'm implementing a C# web service that is supposed to accept a custom message including unbounded number of elements.
Originally, the object is defined in a XSD file like below:
<xsd:element name="LogMessage">
<xsd:complexType>
<xsd:sequence>
<xsd:element minOccurs="1" maxOccurs="1" name="avantlog" type="tns:LogEventType">
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:complexType name="LogEventType">
<xsd:sequence>
<xsd:element minOccurs="1" maxOccurs="1" name="context" type="tns:ContextType">
</xsd:element>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ContextType">
<xsd:sequence>
<xsd:element minOccurs="1" maxOccurs="unbounded" name="severity" type="xsd:string">
</xsd:element>
</xsd:sequence>
</xsd:complexType>
And, in a CS file implementing the web service, I prepared a struct for this:
public struct logevent
{
public ContextType context;
public struct ContextType
{
public string[] severity;
}
}
However, when I tried to access an element of the 'serverity' using a line,
String temp = logevent.context.severity.GetValue(0).ToString()
, the program throws a following error:
"Index was outside the bounds of the array."
When I changed the element from 'unbounded' to '1' in the XSD file and also modified 'public string[] severity;' to 'public string severity;', it works.
Can anyone help me to make the web service to accept a message including unbounded numbers of elements?
The code that corresponds to specified XSD (if serialized using XmlSerializer) is the following:
[XmlRoot("LogMessage"]
public class LogMessage
{
[XmlElement("avantlog")]
public LogEventType AvantLog {get; set;}
}
public class LogEventType
{
[XmlArray("context")]
[XmlArrayItem("severity")]
public string[] Severity {get; set;}
}
You may have to use attributes in order to control the deserialization of the incoming XML. By default, the supported XML structure for arrays follows the form:
<Elements>
<Element>X</Element>
<Element>Y</Element>
</Element>
However, your WSDL specifies unbounded "Element" terms and does not provide for a parent "Elements" block. My understanding is that in order to use unbounded terms, you need to specify attributes to control the deserialization, as unbounded terms are not the default in .NET WSDL generation and deserialization.
This article discusses how to control deserialization using attributes:
http://msdn.microsoft.com/en-us/library/2baksw0z.aspx
You can convert your XSD to a POCO object using "XSD.exe" and then use XmlSerializer. This will make it easy to interact with multiple external systems via XML. Might want to use SGen.exe as well to increase XmlSerializer Performance. Hope this Helps
http://msdn.microsoft.com/en-us/library/x6c1kb0s(v=vs.71).aspx
http://www.jonasjohn.de/snippets/csharp/xmlserializer-example.htm

Categories

Resources