Creating XML childs, omit parent node - c#

Accordingly to MSDN docs:
You can also serialize an array as a flat sequence of XML elements by applying a XmlElementAttribute to the field returning the array as follows.
Desired XML schema:
<xs:element minOccurs="0" maxOccurs="unbounded" name="ResponseLineTest" type="OrderLn" />
C#:
[XmlElementAttribute("ResponseLineTest")]
public OrderLn[] ResponseLine { get; set; }
However: Using .NET 4.51 I get this schema:
<xs:element minOccurs="0" name="ResponseLine" nillable="true" type="tns:ArrayOfOrderLn"/>
https://learn.microsoft.com/en-us/dotnet/standard/serialization/controlling-xml-serialization-using-attributes#serializing-an-array-as-a-sequence-of-elements
HOW do I mark my C# classes and properties, so the WSDL output looks as above (and the documentation) ?

TL;DR Your problem is that .NET has two separate XML serializers, XmlSerializer and DataContractSerializer, and you are creating a WCF service, which uses DataContractSerializer by default. You need to switch to using XmlSerializer by applying XmlSerializerFormatAttribute to your service contract.
Details as follows. Say you have the following WCF service contract (not shown in your question):
public class Output
{
[XmlElementAttribute("ResponseLineTest")]
public OrderLn[] ResponseLine { get; set; }
}
public class OrderLn
{
public string Order { get; set; }
}
[ServiceContract(Namespace = "Question59659046")]
[XmlSerializerFormat]
public interface IQuestion59659046Service
{
[OperationContract]
Output GetOutput(string input);
}
You would like the XML generated by this service to have a schema that includes a repeating sequence of <ResponseLineTest> elements, e.g. like the following:
<xs:complexType name="Output">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" name="ResponseLineTest" type="tns:OrderLn" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="OrderLn">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="1" name="Order" type="xs:string" />
</xs:sequence>
</xs:complexType>
And so you apply the XmlElement attribute to ResponseLine to force it to be serialized to XML in this manner. But, when you generate a WSDL for your service, you instead get a schema that looks like:
<xs:complexType name="Output">
<xs:sequence>
<xs:element minOccurs="0" name="ResponseLine" nillable="true" type="tns:ArrayOfOrderLn" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="ArrayOfOrderLn">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" name="OrderLn" nillable="true" type="tns:OrderLn" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="OrderLn">
<xs:sequence>
<xs:element minOccurs="0" name="Order" nillable="true" type="xs:string" />
</xs:sequence>
</xs:complexType>
<xs:element name="OrderLn" nillable="true" type="tns:OrderLn" />
The schema includes an extra intermediate type ArrayOfOrderLn and the overridden element name "ResponseLineTest" was not used. Apparently the [XmlElementAttribute("ResponseLineTest")] attribute was completely ignored. Why might this be?
As it turns out, this behavior is documented in Using the XmlSerializer Class, which explains that your service is not using XmlSerializer at all, but rather a different serializer that ignores the [XmlElement] attribute:
By default WCF uses the DataContractSerializer class to serialize data types.
<<snip>>
At times, you may have to manually switch to the XmlSerializer. This happens, for example, in the following cases:
When precise control over the XML that appears in messages is important...
In this case, precise control over the XML is required, specifically to force the ResponseLine property to be serialized without an outer container element. However, serializing a collection without an outer container element is not supported by DataContractSerializer, so you must switch to using XmlSerializer by applying [XmlSerializerFormat] to your service contract:
[ServiceContract(Namespace = "Question59659046")]
[XmlSerializerFormat]
public interface IQuestion59659046Service
{
[OperationContract]
Output GetOutput(string input);
}
Now the WSDL generated for your service will be as required.

Necessary changes in your webservice definition
First of all the definition of the necessary complex type PlaceOrder does not mention an element of the type tns:OrderLine. I guess you have to change the definition of the element. Because your webservice is very loosely defined, it works like you 've shown in your question.
Here 's the current definition of the PlaceOrder request. This says, that the PlaceOrder element is required as request parameter.
<wsdl:message name="IService_PlaceOrder_InputMessage">
<wsdl:part name="parameters" element="tns:PlaceOrder"/>
</wsdl:message>
The current definition of the PlaceOrder complex type shows, that there is no OrderLine element.
<xs:element name="PlaceOrder">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" name="userToken" nillable="true" type="xs:string"/>
<xs:element minOccurs="0" name="ediOrder" nillable="true" type="q1:order"/>
</xs:sequence>
</xs:complexType>
</xs:element>
This means, that you can send everything in addition. Your webservice does not know an OrderLine element in the PlaceOrder context because it is not defined here. You have to change the definition of the PlaceOrder element into the following notation.
<xs:element name="PlaceOrder">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" name="userToken" nillable="true" type="xs:string"/>
<xs:element minOccurs="0" name="ediOrder" nillable="true" type="q1:order"/>
<xs:element minOccurs="0" name="OrderLine" nillable="true" type="q1:ArrayOfOrderLine"/>
</xs:sequence>
</xs:complexType>
</xs:element>
The definition of ArrayOfOrderLine is defined as follows:
<xs:complexType name="ArrayOfOrderLine">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" name="OrderLine" nillable="true" type="tns:OrderLine"/>
</xs:sequence>
</xs:complexType>
This definition says, that you want the OrderLine complex types with a parent node OrderLine. So the parent node occurs exactly as defined in your wsdl file. To omit the parent node you have to redefine the PlaceOrder complex type as follows:
<xs:element name="PlaceOrder">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" name="userToken" nillable="true" type="xs:string"/>
<xs:element minOccurs="0" name="ediOrder" nillable="true" type="q1:order"/>
<xs:element minOccurs="0" maxOccurs="unbounded" name="OrderLine" nillable="true" type="tns:OrderLine"/>
</xs:sequence>
</xs:complexType>
</xs:element>
This new definition shows that the "OrderLine" element cannot be named or can be named more than once. The parent node in this case is PlaceOrder.
A possible PHP example
Soap follows a strictly object-oriented approach. Based on this understanding, you also have to work with objects in PHP. First you need value objects (sometimes called entities) based on your xsd/wsdl definition. Keep in mind, that this example uses the redefined PlaceOrder definition.
<?php
namespace Webservice\Entity;
use ArrayObject;
use SoapVar;
class PlaceOrder
{
protected $userToken;
protected $ediOrder;
protected $OrderLine;
public function __construct()
{
$this->OrderLine = new ArrayObject();
}
public function getUserToken(): ?SoapVar
{
return $this->userToken;
}
public function setUserToken(SoapVar $userToken): self
{
$this->userToken = $userToken;
return $this;
}
public function getEdiOrder() : ?SoapVar
{
return $this->ediOrder;
}
public function setEdiOrder(SoapVar $ediOrder): self
{
$this->ediOrder = $ediOrder;
return $this;
}
public function getOrderLine(): ArrayObject
{
return $this->OrderLine;
}
public function attachOrderLine(SoapVar $orderLine): self
{
$this->orderLine->append($orderLine);
return $this;
}
public function setOrderLine(ArrayObject $orderLine): self
{
$this->OrderLine = $orderLine;
return $this;
}
}
The above shown PHP code shows the PlaceOrder value object. As you can see all elements, which are defined in your webservice definition, occur as properties of this class. This class is an exact php implementation of the PlaceOrder complex type. You can say that all complex types are always PHP classes. Further the accepted method parameters are mainly SoapVar instances. This is important for the soap client because this guarantees the right xml structure at the end.
The OrderLine value object ...
<?php
namespace Webservice\Entity;
class OrderLine
{
protected $AdditionalCustomerReferenceNumber;
protected $LineID;
protected $OrderedArticle;
protected $PortalReference;
// getters and setters here
}
With this two classes one can do a full webservice call with PHP. The following example is not testet and shows how to work with the PHP SoapClient class. The class is sometimes a bit deceptive and it takes a bit of work to get the right result at the end. But mainly this is the way how to work it.
<?php
namespace Wesbervice;
use Webservice\Entity\Order;
use Webservice\Entity\OrderLine;
use Webservice\Entity\PlaceOrder;
use SoapFault;
use SoapVar;
try {
// this url contains the wrong defined PlaceOrder complex type
$wsdl = 'https://uat-salesapi.ndias.com/service.svc?singlewsdl&version=27';
$client = new SoapClient($wsdl, [
'cache_wsdl' => WSDL_CACHE_NONE, // as long as you work on your wsdl
'encoding' => 'utf-8',
'exceptions' => true,
'soap_version' => SOAP_1_1,
'trace' => true, // enables tracing and __getLastRequest() / __getLastResponse()
'classmap' => [
'order' => Order::class,
'OrderLine' => OrderLine::class,
'PlaceOrder' => PlaceOrder::class,
],
]);
// user token
$usertoken = new SoapVar('bla', XSD_STRING, '', '', 'userToken', 'http://tempuri.org/');
// edi order
$order = (new Order())
->setBlanketOrderReference(new SoapVar(...))
->setBuyerParty(new SoapVar(...);
$order = new SoapVar($order, SOAP_ENC_OBJECT, '', '', 'ediOrder', 'http://tempuri.org/');
// compile our request parameter
$parameter = (new PlaceOrder())
->setUserToken($usertoken)
->setEdiOrder($order);
// order list objects
$orderLine1 = (new OrderLine())
->setAdditionalCustomerReferenceNumber(new SoapVar(...))
->setLineID(new SoapVar(...));
$orderLine1 = new SoapVar($orderLine1, SOAP_ENC_OBJECT, '', '', 'OrderLine', 'http://tempuri.org/');
$parameter->attachOrderLine($orderLine1);
$orderLine2 = (new OrderLine())
->setAdditionalCustomerReferenceNumber(new SoapVar(...))
->setLineID(new SoapVar(...));
$orderLine2 = new SoapVar($orderLine2, SOAP_ENC_OBJECT, '', '', 'OrderLine', 'http://tempuri.org/');
$parameter->attachOrderLine($orderLine2);
$parameter = new SoapVar($parameter, SOAP_ENC_OBJECT, '', '', 'PlaceOrder', 'http://tempuri.org/');
// the client knows the PlaceOrder method from the wsdl
$result = $client->PlaceOrder($parameter);
// the result is a stdClass structure, als long as the classmap parameter does not contain definitions of type to php entity classes
echo "<pre>";
var_dump($result);
echo "</pre>";
} catch (SoapFault $fault) {
echo "<pre>";
var_dump($fault);
echo "</pre>";
}
Conclusion
Your web service is very imprecisely defined. For this reason, you should simply rethink the definitions for the parameters and define them more precisely in the WSDL file. Then it works with PHP, too. PHP uses strictly web standards in its soap extension.

Related

Accepting 2 complex type arguments in Spyne vs Core WCF

I need to create a SOAP service which needs to be consumed by a third-party application to accomplish a task (callback). The below is the C# interface they've specified in their documentation and I think they've already developed the consumer so it is my responsibility to create the server according to the specification.
public interface ICallback
{
void Delivery(Info info, Item[] items)
string TestCallback(string data)
}
Additionally the below are how Info and Item classes look like:
class Info
{
public int Id {get; set;}
public string Name {get; set;}
}
class Item
{
public int Id {get; set;}
public string Name {get; set;}
public string Value {get; set;}
}
To create the service in spyne I wrote the below code
from spyne import (
Application,
rpc,
ServiceBase,
Iterable,
Integer,
Unicode,
ComplexModel,
)
from spyne.protocol.soap import Soap11
from spyne.server.wsgi import WsgiApplication
import logging
logging.getLogger().setLevel(logging.INFO)
class Info(ComplexModel):
Id = Integer
Name = Unicode
class Item(ComplexModel):
Id = Integer
Name = Unicode
Value = Unicode
class Callback(ServiceBase):
#rpc(Info, Iterable(Item))
def Delivery(ctx, info, item):
logging.info(f">>>> Info - {info}")
for i in range(item):
logging.info(f">>>>>>>> item - {i}")
#rpc(Unicode, _returns=Unicode)
def TestCallback(ctx, data):
logging.info(f">>>> Test - {data}")
return data
application = Application(
[Callback],
"spyne.examples.hello.soap",
in_protocol=Soap11(validator="lxml"),
out_protocol=Soap11(),
)
wsgi_application = WsgiApplication(application)
And noticed spyne wraps the arguments to a single class. The callback service from the third-party API is not aware of this wrapping so it is failing. I presume this is how spyne works and felt like the maintainers of spyne should have perhaps acknowledged this limitation.
However a server created using Core WCF gives me the wsdl which does not wrap the argument rather just keep them as they are. So the callback service which is provided by the third party API just works as expected.
I went to compare the generated XML and found spyne generates the following xml for the particular operation:
<wsdl:operation name="Delivery" parameterOrder="Delivery">
<wsdl:input name="Delivery" message="tns:Delivery"/>
<wsdl:output name="DeliveryResponse" message="tns:DeliveryResponse"/>
</wsdl:operation>
In the <wsdl:types> the below is how the Delivery is specified (this is where the wrapping happens)
<xs:complexType name="Delivery">
<xs:sequence>
<xs:element name="info" type="s0:Info" minOccurs="0" nillable="true"/>
<xs:element name="items" type="s0:Item" minOccurs="0" nillable="true"/>
</xs:sequence>
</xs:complexType>
...
<xs:element name="Delivery" type="tns:Delivery"/>
However the Core WCF generates a separate wsdl which looks like below:
<wsdl:operation name="Delivery">
<wsdl:input wsaw:Action="http://tempuri.org/IService/Delivery" message="tns:IService_Delivery_InputMessage"/>
<wsdl:output wsaw:Action="http://tempuri.org/IService/DeliveryResponse" message="tns:IService_Delivery_OutputMessage"/>
</wsdl:operation>
...
<wsdl:message name="IService_Delivery_InputMessage">
<wsdl:part name="parameters" element="tns:Delivery"/>
</wsdl:message>
...
<xs:element name="Delivery">
<xs:complexType>
<xs:sequence>
<xs:element xmlns:q2="http://schemas.datacontract.org/2004/07/API.Receiver" minOccurs="0" name="info" nillable="true" type="q2:Info"/>
<xs:element xmlns:q3="http://schemas.datacontract.org/2004/07/API.Receiver" minOccurs="0" name="item" nillable="true" type="q3:item"/>
</xs:sequence>
</xs:complexType>
</xs:element>
Here the key difference is spyne keeps name for the complex type (<xs:complexType>), which is resulting in the wrapper while Core WCF avoids the name. Additionally according to this documentation, the name is only required if the containing element is the schema element; otherwise, prohibited.
The above is my findings and I just want to validate if my understanding is correct. Can anyone please let me know if this is correct?
Additionally, I also would like to know either spyne or Core WCF follows the W3C standard?
I thought going over your question point-by-point would be easiest, so here we go:
And noticed spyne wraps the arguments to a single class.
This is how soap works. The parent tag's namespace (ie. the immediate child of the soap:Envelope element)'s namespace and name taken together forms the name of the remote procedure you are calling.
The following schema fragment
<xs:complexType name="Delivery">
<xs:sequence>
<xs:element name="info" type="s0:Info" minOccurs="0" nillable="true"/>
<xs:element name="items" type="s0:Item" minOccurs="0" nillable="true"/>
</xs:sequence>
</xs:complexType>
...
<xs:element name="Delivery" type="tns:Delivery"/>
and the following schema fragment
<xs:element name="Delivery">
<xs:complexType>
<xs:sequence>
<xs:element xmlns:q2="http://schemas.datacontract.org/2004/07/API.Receiver" minOccurs="0" name="info" nillable="true" type="q2:Info"/>
<xs:element xmlns:q3="http://schemas.datacontract.org/2004/07/API.Receiver" minOccurs="0" name="item" nillable="true" type="q3:item"/>
</xs:sequence>
</xs:complexType>
</xs:element>
are equivalent.
Here the key difference is Spyne keeps name for the complex type (<xs:complexType>), which is resulting in the wrapper while Core WCF avoids the name.
This is not correct. Spyne serializes schema elements separately in a reusable way (so that it's serialized only once) whereas .net seems to embed the types. Both are valid approaches.
Additionally according to this documentation, the name is only required if the containing element is the schema element; otherwise, prohibited.
The parent of the xs:complexType tag in the spyne example is indeed the <schema> element, so there are no issues here.
Additionally, I also would like to know either spyne or Core WCF follows the W3C standard?
Can't speak about what .NET does, but Spyne implements a subset of the W3C's XML Schema 1.0 standard (among other ones). I've made every effort for the implemented parts to be conformant. I'm happy to fix any deviations and welcome patches that add the missing bits.

I need to create an object that can take the key value pair similar to this xsd

This is an example of an XSD for one of the templates that will be stored in the database. The "to_email", "first_name" etc are all tokens and I need to dynamically create a dictionary or an object that can be returned to the client that will give a list of these tokens. The idea behind this is if there is any change in a template then we will just insert another value in the database and it should automatically be created dynamically for returning it to client when they will query for this template.
How do I go about creating/parsing this?
I don't want create a class object with individual element as that would mean that I will have to change it for every addition of the elements. So it has to be something generic.
We should be able to return the set as JSON or XML based on what the client asks for.
How do I go about doing this?
Any help would be appreciated.
Thank you in advance.
<?xml version="1.0" encoding="utf-8"?>
<xs:schema targetNamespace="http://tempuri.org/XMLSchema.xsd"
elementFormDefault="qualified" xmlns="http://tempuri.org/XMLSchema.xsd"
xmlns:mstns="http://tempuri.org/XMLSchema.xsd"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="claim">
<xs:complexType>
<xs:sequence>
<xs:element name="to_email" type="xs:string"/>
<xs:element name="first_name" type="xs:string"/>
<xs:element name="last_name" type="xs:string"/>
<xs:element name="url" type="xs:string"/>
<xs:element name="received_date" type="xs:date"/>
<xs:element name="contact_number" type="xs:string"/>
<xs:element name="employer_name" type="xs:string"/>
<xs:element name="er_label" type="xs:string"/>
<xs:element name="er_flag" type="xs:integer"/>
<xs:element name="benefit_id" type="xs:integer"/>
<xs:element name="employee_id" type="xs:integer"/>
<xs:element name="employer_id" type="xs:integer"/>
<xs:element name="form_id" type="xs:integer"/>
<xs:element name="er_url" type="xs:string"/>
<xs:element name="tax" type="xs:string"/>
<xs:element name="Repeater" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="form_id" type="xs:integer"/>
<xs:element name="amount" type="xs:integer"/>
<xs:element name="expense_name" type="xs:string"/>
<xs:element name="date_of_service" type="xs:string"/>
<xs:element name="status_of_claim" type="xs:string"/>
<xs:element name="status_reason"/>
<xs:element name="Order_by" type="xs:integer"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
Adding further comments:
The response to the client should be something like each property of the object representing a field. For example a object.
{
‘field1’: <FieldDefinition>
‘field2’: <FieldDefinition>
‘field3’: <FieldDefinition>
.
.
.
}
The object would contains the following properties like Type for example if it is a string or int, Display name i.e. the internal name could be first_name but the display name would FirstName# etc.
Also the could be an array as well. So this makes it little more complex.
Am I making any sense?
I believe you can do the XML processing or you can find that in Google. Plenty of examples are there. I just added the code for logic you have mentioned for the properties. //Your XML processing
var fields = new List<FieldDefinition>
{
new FieldDefinition{ Type="string", DisplayName="FirstName"},
new FieldDefinition{ Type="int", DisplayName="EmployeeId"}
};
var infr = new List<Infrastructure>
{
new Infrastructure { def1=fields.FirstOrDefault()}// loop through to assign each item
};
foreach (var item in infr)
Console.WriteLine(item.def1.DisplayName + " -" + item.def1.Type);
Console.Read();
}
}
public class Infrastructure
{
public FieldDefinition def1 { get; set; }
}
public class FieldDefinition
{
public string Type { get; set; }
public string DisplayName { get; set; }
}
You generate classes for them using the xsd.exe util
Seen here:
How to generate .NET 4.0 classes from xsd?
xsd your.xsd /classes
You can parse your template using XPath query or any generic XML processing. For any template change, you can opt for CacheDependency for File. You just cache the whole xml and if any changes to the tag/file then you insert that portion to the database and reload the cache. You can try SQLDependency also to get similar work.
Another option, you can go for filesystemwatcher which will raise event for changing in the file content. Then you just need to insert the added portion in the database.

Not all properties are being sent or received in a WCF call

I am having a frustrating time debugging my new WCF service. Issue is with a certain DataContract. Seems like on de-serialization on the server side after a certain property something happens and the rest are not set. I have looked at the generated xsd schemas generated from the metadata and all seems ok. I have used Fiddler to snoop the soap packet sent and leaving the client all data is present. I have even set the Order parameter to the DataContract attribute to see if i could see a pattern but never did. I have also compared the DataMember name with the one in the message and they all match. The only pattern i found was this:
Here is the xsd definition for the DataContract object in question:
<xs:complexType name="Attachment">
<xs:sequence>
<xs:element minOccurs="0" name="dateTime" type="xs:dateTime"/>
<xs:element minOccurs="0" name="description" nillable="true" type="xs:string"/>
<xs:element minOccurs="0" name="fileName" nillable="true" type="xs:string"/>
<xs:element minOccurs="0" name="guid" type="ser:guid"/>
<xs:element minOccurs="0" name="obsDate" type="xs:dateTime"/>
<xs:element minOccurs="0" name="operation" nillable="true" type="xs:string"/>
<xs:element minOccurs="0" name="originalFileName" nillable="true" type="xs:string"/>
<xs:element minOccurs="0" name="title" nillable="true" type="xs:string"/>
<xs:element minOccurs="0" name="type" nillable="true" type="xs:string"/>
</xs:sequence>
</xs:complexType>
Here is the DataContract object:
[DataContract(Namespace="http://www.myns.com")]
public class Attachment
{
public enum AttachmentSortOrder { Date, FileType }
[DataMember]
public Guid guid;
[DataMember]
public DateTime dateTime;
[DataMember]
public string operation;
[DataMember]
public DateTime obsDate;
[DataMember]
public string originalFileName;
[DataMember]
public string fileName;
[DataMember]
public string title;
[DataMember]
public string type;
[DataMember]
public string description;
}
Seems like all properties after obsDate does not get set for some reason.
WHY?
It would help to see the SOAP and also know whether the client is .NET.
I've seen something like this happen using a non .NET client. Your xsd specifies a sequence - which is be determined using the Order attribute or defaulting to alphabetical order. Some clients seem to treat this as an all element; they appear to take the view that "it's all the same XML" (see In XML, is order important?).
So in summary, check whether the SOAP elements are in the correct sequence matching your XSD.
Enable WCF Error logging and findout the root cause via SvcTraceViewer.exe

Error on serializing XML

Hi I am getting this error, when serializing to XML
Unable to generate a temporary class (result=1).
error CS0030: Cannot convert type 'XYZ.ProducerChannel[][]' to 'XYZ.ProducerChannel[]'
error CS0029: Cannot implicitly convert type 'XYZ.ProducerChannel[]' to 'XYZ.ProducerChannel[][]'
Can someone help me with whats going on?
Depending classes:
/// <remarks/>
[System.Xml.Serialization.XmlArrayItemAttribute("CarrierAppointment", typeof(ProducerChannel[]), IsNullable=false)]
[System.Xml.Serialization.XmlArrayItemAttribute("DistributionChannelInfo", typeof(ProducerChannel), IsNullable=false, NestingLevel=1)]
public ProducerChannel[][][] Producer
{
get
{
return this.producerField;
}
set
{
this.producerField = value;
}
}
Usage:
var producer = new InitialPurchaseOrder.ProducerChannel{DistributionChannelName = division};
bdParty.Producer = new InitialPurchaseOrder.ProducerChannel[][][]// {{},{},{producer}};
{new InitialPurchaseOrder.ProducerChannel[][]
{new InitialPurchaseOrder.ProducerChannel[]{producer}}};
Error Line:
var serializer = new System.Xml.Serialization.XmlSerializer(txLife.GetType());
Related XSD:
<xs:element name="Policy" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="Annuity" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="QualPlanType" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="tc" form="unqualified" type="xs:string" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
Please let me know how to fix this.
Thanks,
Sounds like a bug in xsd.exe I have stumbled upon a few times now, it doesn't handle arrays of arrays correctly.
If that's the case you have to modify the generated classes so that the XmlSerializer can work with it.
Either the attributes or the type of the Producer property is not correct.
If you had posted the relevant section of the XML schema I could say it for certain, but try to change the type of the Producer property from ProducerChannel[][][] to ProducerChannel[][].
If you can modify the XML schema maybe this link can help without the need to modify the C# classes after generating them.
I think the error message is pretty clear. You have the property as ProducerChannel[][] (or ProducerChannel[][][]), but your XmlArrayItem attribute is trying to serialize it as ProducerChannel[].

WSDL.exe generated client gives: Unhandled Exception: System.InvalidOperationException: There is an error in XML document (...)

I have a WebService that returns complex objects.
public class Resource;
public class Asset extends Resource;
public class Collection extends Resource;
The methods that return the subclasses work fine:
Asset[] getAssets();
Collection[] getCollections();
However the methods that return the base class are generating an exception:
Resource[] getResources();
The WebService itself is written in java using CXF but I dont think that matters. I am able to use even the base class methods with a sample client created with java.
Here is the exception using .Net Client:
`Unhandled Exception: System.InvalidOperationException: There is an error in XML document (1, 173). ---> System.InvalidOperationException: The specified type was not recognized: name='asset', namespace='http://www.turimsaint.com/', at <return xmlns=''>`.
Here is the client Code Generated by wsdl.exe
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("", RequestNamespace = "http://www.turimsaint.com/", ResponseNamespace = "http://www.turimsaint.com/", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
[return: System.Xml.Serialization.XmlElementAttribute("return", Form = System.Xml.Schema.XmlSchemaForm.Unqualified)]
public Resource[] getCollectionContents([System.Xml.Serialization.XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified)] Collection p_collection)
{
object[] results = this.Invoke("getCollectionContents", new object[] {
p_collection});
return ((Resource[])(results[0]));
}
And lastly this is the wsdl for the related elements:
- <xs:complexType name="collection">
- <xs:complexContent>
- <xs:extension base="tns:resource">
- <xs:sequence>
<xs:element name="_instructor" type="xs:string" />
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
- <xs:complexType abstract="true" name="resource">
- <xs:sequence>
<xs:element name="_title" type="xs:string" />
<xs:element name="_deepLink" type="xs:string" />
<xs:element name="_id" type="xs:string" />
<xs:element name="_description" type="xs:string" />
<xs:element name="_type" type="xs:string" />
<xs:element maxOccurs="unbounded" name="_metadata" type="tns:resourceMetadata" />
</xs:sequence>
</xs:complexType>
....
- <xs:complexType name="getResourceByIDResponse">
- <xs:sequence>
<xs:element minOccurs="0" name="return" type="tns:resource" />
</xs:sequence>
</xs:complexType>
Any help appreciated.
Thanks.
This is digging a little, its been a while since playing with .Net 2.0 style web service clients.
If i remember correctly, you´ll have to do some attribute work. You´ll have to inform the base class ´resource´ about the possibly derived classes ´Asset´-´Collection´ or methods returning one of the derived classes by placing attributes on top.
Have a look at
http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlincludeattribute.aspx
Hope this helps,

Categories