Svcutil Not Generating All Classes - c#

I want to call an external SOAP service which i want to generate proxy classes for. The .wsdl is not exposed online, but provided alongside a set .xsd files. I'm then using svcutil (also tried with dotnet-svcutil 1.0.4) to generate the classes. Consider the following XML:
<xs:complexType name = "ValidationReply" abstract="false">
<xs:complexContent>
<xs:extension base="common:Reply">
<xs:sequence>
<xs:element name="data" type="ns:ValidationReplyData" minOccurs="0" maxOccurs="1"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="ValidationReplyData" abstract="false">
<xs:sequence>
<xs:element name="errors" type="ns:Error" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
Generates the following code:
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("dotnet-svcutil", "1.0.4")]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="Namespace")]
public partial class ValidationReply: Reply
{
private Error[] dataField;
/// <remarks/>
[System.Xml.Serialization.XmlArrayAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=0)]
[System.Xml.Serialization.XmlArrayItemAttribute("errors", Form=System.Xml.Schema.XmlSchemaForm.Unqualified, IsNullable=false)]
public Error[] data
{
get
{
return this.dataField;
}
set
{
this.dataField = value;
}
}
}
The issue is that the XML defines an inner type containing the list of errors, where Svcutil puts the list of errors directly as the data field, instead making the data field of type ValidationReplyData. The ValidationReplyData is not generated at all.
The code compiles without any issues and i am able to call the external service without errors. The data field is however always null as the response is on the format:
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<fl:ValidationReply xmlns:fw="Namespace" >
<data>
<errors>
<code>236</code>
</errors>
<errors>
<code>237</code>
</errors>
</data>
</fl:ValidationReply>
</S:Body>
</S:Envelope>
Which does not deserialize correctly to the generated class.
In short, does anyone know how to force Svcutil to generate all classes? Redundant or not.
I have simplified the xml snippets, so there may be some inconsistencies in the example, but the digest of the problem is the missing ValidationReplyData class-

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.

Creating XML childs, omit parent node

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.

Using non default types within a xsd definition for xsd2code when generating C# code

In our project we are using Xsd2Code to generate c# code.
The XSD I am trying to adjust looks like this:
<?xml version="1.0"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="File">
<xs:complexType>
<xs:sequence>
<xs:element name="Version" type="xs:string" />
<xs:element name="Language" type="xs:string" />
<xs:element name="Component" type="xs:anyType" minOccurs="1" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
Within our project we now defined an abstract class Version. Is it possible to use our own class Version instead of string as the type of the element Version within the Xsd?
We would like to generate as many classes as possible using Xsd2Code. That's why I want to avoid to also having to create the File class by hand.
Thanks in advance for any help.
You can create a new abstract class Version in xsd scheme and then make your defined Version class as partial and put it to the same namespace as the generated classes. By default all your generated classes must be partial.

XML serialization with c# & windows forms

I am trying to create a waypoint generator using windows forms, that allows a user to create waypoint & path data via a GUI & output the data to an XML file. I've chosen to take advantage of the built in c# XML serialization feature, but have been unable to format the XML output in the way required by the client.
A stripped version of the waypoint data object would look something like the following:
// Waypoint data class
[XmlRoot("RootNode")]
public class WaypointProjectData
{
[XmlElement("Map")] // Also tried to use XmlElementAttribute,
[XmlAttribute("file")] // XmlAttributeAttribute, and many variations
// of these qualifiers, with no success
public string m_szMapImageFileName;
[XmlAttribute("width")]
public int m_iWidth;
[XmlAttribute("height")]
public int m_iHeight;
[XmlArray("Nodes")]
public ArrayList m_aoNodes;
WaypointProjectData()
{
m_szMapImageFileName = "map.png";
m_aoNodes = new ArrayList();
}
}
The client requires that the XML output conforms with the following layout/format:
<RootNode>
<Map file="map.png" width="100" height="100" />
<OtherData var="variable" data="10" />
<Nodes>
<Node x="10" y="30" />
<Node x="30" y="20" /> // etc...
</Nodes>
</RootNode>
Some clear examples on how to do this using c# xml serialization would be very helpful. Thanks!
When I have to serialize something in a specific Xml format like this, I start with a schema (inferred if necessary, hand-crafted if known). Then I use the VS xsd tool to create the serialization classes (and yes, I know xsd can be used for the inferring schema part—I just never bother with it for that purpose).
So if you have an xsd file like this one created from your example (note that I specified data types as much as possible—this helps the xsd tool to use the appropriate types for properties):
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
<xs:element name="RootNode">
<xs:complexType>
<xs:sequence>
<xs:element name="Map">
<xs:complexType>
<xs:attribute name="file" type="xs:string"/>
<xs:attribute name="width" type="xs:decimal"/>
<xs:attribute name="height" type="xs:decimal"/>
</xs:complexType>
</xs:element>
<xs:element name="OtherData">
<xs:complexType>
<xs:attribute name="var" type="xs:string"/>
<xs:attribute name="data" type="xs:decimal"/>
</xs:complexType>
</xs:element>
<xs:element name="Nodes" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="Node" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="x" type="xs:decimal"/>
<xs:attribute name="y" type="xs:decimal"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
If you name this Root.xsd, then you can go to the VS command line and run
xsd Root.xsd /c /n:myProject.Xml
That'll create a class file (named Root.cs because that's the default when processing Root.xsd) that contains objects you can easily plug into .Net to serialize and deserialize the XML. Note that I specified the namespace the classes will have ("/n:myProject.Xml"). I prefer to control that namespace, but defaults are usually fine for the rest. Further, the tool creates partial classes, so if you want custom property accessors, you're fine to add them in a separate file that won't get creamed if you need to run the tool again.
Another tip, I create a text file in my project with the name "<xsd name> xsd Command Line.txt". That way I just have to paste that into the VS command line and don't have to remember everything else I used.
As soon as you go off the beaten track serialisation is a huge PIA.
You need something to mark up as map, probably OtherData as well, so that means you need a class or struct that corresponds to the node.
e.g. WaypointProejctdata as a class Map, that has properties filename, width and height.
My general rule of thumb, is as soon as I have to start messing with my objects to get the xml, serialisation gets binned,and I add an interface that takes an XmlReader or Writer and implement it.
Tryng to get serialisation to do what you want is usually way more code and far less comprehensible than doing that.

System.InvalidOperationException : XmlSerializer attribute System.Xml.Serialization.XmlChoiceIdentifierAttribute is not valid in Item

I have been trying to connect to some web services using WCF but I keep getting an error when I try to call the function I need.
This is the error I'm getting:
System.InvalidOperationException : XmlSerializer attribute System.Xml.Serialization.XmlChoiceIdentifierAttribute is not valid in Item. Only XmlElement, XmlArray, XmlArrayItem, XmlAnyAttribute and XmlAnyElement attributes are supported when IsWrapped is true.
The error happens before it even gets to calling the actual service and it doesn't even occur because of the method I'm trying to call. The issue is with another method that's defined in the WCF generated class.
I have been able to trace the issue to a section of code in the XSD that is used to define the WSDL:
<xs:choice minOccurs="0">
<xs:element name="additionalSocInd" type="tns:BinaryExpressionType"/>
<xs:element name="skipServiceValidationInd" type="tns:BinaryExpressionType"/>
</xs:choice>
The corresponding generated code:
[System.CodeDom.Compiler.GeneratedCodeAttribute("svcutil", "4.0.30319.1")]
[System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute(Namespace="http:integration.sprint.com/interfaces/manageSubscriberServices/v1/manageSubscr" +
"iberServices.xsd", IncludeInSchema=false)]
public enum ItemChoiceType2
{
additionalSocInd,
skipServiceValidationInd,
}
When I comment out the above enum and all references to it, the service works. There are other xs:choice statements in the XSD that don't seem to cause any problems.
Update:
Further investigation revealed that when you have the following:
The element is defined directly inside of a sequence element:
<xs:sequence>
<xs:element ... />
...
<xs:choice minOccurs="0">
<xs:element name="additionalSocInd" type="tns:BinaryExpressionType" />
<xs:element name="skipServiceValidationInd" type="tns:BinaryExpressionType" />
</xs:choice>
...
<xs:element ... />
</xs:sequence>
The proxy generated by svcutil causes the error noted above.
When changed to look like this:
<xs:sequence>
<xs:element ... />
...
<xs:element minOccurs="0" name="myChoiceType" type="tns:MyChoiceType" />
...
<xs:element ... />
</xs:sequence>
<xs:complexType name="MyChoiceType">
<xs:choice>
<xs:element name="additionalSocInd" type="tns:BinaryExpressionType" />
<xs:element name="skipServiceValidationInd" type="tns:BinaryExpressionType" />
</xs:choice>
</xs:complexType>
The error goes away. So it may be a bug with the code the generator (svcutil) generates.
I will need to call all the methods in the WSDL, so commenting out the ones that don't work isn't an option. And I need to get it working without changing the WSDL (which is the client's, not ours). Any help would be appreciated.
Try to generate the proxy from command line with these flags:
svcutil /wrapped /serializer:XmlSerializer http://wsdl_url/

Categories