XML Array Serialization - c#

I'm using client methods for soap web service. On one of the methods it has a parameter as string[] list, so i create...
string[] myList = { "12345678" };
and i send off the request via the client method but i get an error saying SAXException Found character data inside an array element while deserializing. I know the client method sends this inside the envelope.
<List>12345678</List>
Which is supposed to be like this...
<List><string>12345678</string></List>
I've tried the following and still do not get the result which i need.
[XmlArrayItem("m")]
public string[] list
{
get; set;
}
i did this to use the class above,
string[] a = new string[] { "12345678" };
list = a;
and the result is the same,
<List>12345678</List>

I had to modify the wsdl in the type it had maxOccurs="Unbounded" type="xsd:string" i changed it to minOccurs="0" maxOccurs="1" type="xsd:ArrayOfString" and regenerate the client code. And it worked. Thanks for the suggestions.

Related

Problems adding Any-Element to dynamically created XSD-Schema

I just wanna add an Any-element node to an existing XSD-Schema created by this code particle:
private void CreateSchema()
{
//This function returns the XML Schema definition of a XML Element by using the Generation function of a Dataset
XmlSchemaInference x_scheme = new XmlSchemaInference();
this.XsDSchemaSet = x_scheme.InferSchema(this.myXmlElement.CreateReader());
this.XsDSchemaSet.Compile();;
}
After I created the XSD-Schemaset some parts have to be modified. The following code sets the Min- and max-Occurs attributes of existing elements which also works fine.
After modification of the attributes I also have to add an Element of type XmlSchemaElement to the Items-collection of the XmlSchemaSequence like you see in the few lines above the end of the sample code. That does not work. While debugging i can see the element within the Items-collection, but after Reprocessing & recompilation of the Schemaset the modified attributes are set pretty well, but the generated Any-element is not present like you see in the MessageBox of final result. Could anybody help?
private bool ModifyXsdElement(XmlSchemaElement element, XElement myXmlElement)
{
// this function modifies the occurance min an max of the child elements
XmlSchemaSimpleType simpleType = element.ElementSchemaType as XmlSchemaSimpleType;
if (simpleType != null)
{
MessageBox.Show("Function XsdModifyElement: Error - Simple Type!");
return false;
}
else
{
XmlSchemaComplexType complexType = element.ElementSchemaType as XmlSchemaComplexType;
if (complexType != null) //This is a complexType object
{
if (complexType.AttributeUses.Count > 0)
{
//todo: anything if there are attributes
}
bool typeMatch = false;
XmlSchemaSequence sequence = complexType.ContentTypeParticle as XmlSchemaSequence;
if (sequence != null)
{
typeMatch = true;
string fixedValue = string.Empty;
XmlSchemaElement el = new XmlSchemaElement();
foreach (XmlSchemaElement childElement in sequence.Items)
{
//MessageBox.Show("Child Element: " + childElement.Name);
int iOccCtr = GetNoOfXmlChildElements(childElement.Name, myXmlElement);
childElement.MinOccurs = iOccCtr;
childElement.MaxOccurs = iOccCtr;
childElement.MinOccursString = iOccCtr.ToString();
childElement.MaxOccursString = iOccCtr.ToString();
if (FixedValues.TryGetValue(childElement.Name.ToString(), out fixedValue))
childElement.FixedValue = fixedValue;
el = childElement;
}
//Add any element
XmlSchemaAny anyElement = new XmlSchemaAny();
anyElement.MinOccurs = 0;
anyElement.MaxOccurs = 1;
anyElement.ProcessContents = XmlSchemaContentProcessing.Lax;
anyElement.Parent = sequence;
sequence.Items.Add(anyElement);
}
}
}
return true;
}
The final result of the compiled Schema looks like that:
<?xml version=\"1.0\"?>
<xs:schema attributeFormDefault=\"unqualified\" elementFormDefault=\"unqualified\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">
<xs:element name=\"STEP\">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs=\"1\" maxOccurs=\"1\" fixed=\"0002\" name=\"LFDNR\" type=\"xs:unsignedByte\" />
<xs:element minOccurs=\"1\" maxOccurs=\"1\" name=\"FUNKTIONSNUMMER\" />
<xs:element minOccurs=\"1\" maxOccurs=\"1\" fixed=\"Firmwareinformationen lesen\" name=\"FUNKTIONSNAME\" type=\"xs:string\" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
Thanks for your help!
Br Matthias
Your problem is due to your use of a post compilation property. As the help has it:
The particle for the content type. The post-compilation value of the ContentType particle.
In general, one hint in using .NET's SOM API is to look for properties that have a setter as well. "hint" since some are properties are both: post compilation, and user configurable.
If your complex type's definition has an explicit content model (extension or restriction), then you need to use the XmlSchemaComplexType.ContentModel. If it's an XmlSchemaComplexContent, navigate its Content property (one of XmlSchemaComplexContentRestriction or XmlSchemaComplexContentExtension); each of these types have a Particle property, which is the one you can modify.
Otherwise, if there's no content model, simply access the XmlSchemaComplexType.Particle.
ContentTypeParticle is a post-compiled property. Only some attributes like min-/max-Occurs can be modified. To add new nodes, like the any node in this case, the Particle-property must be modified. After reprocessing of the schema and recompilation of the SchemaSet the new element will be added to the post-compiled ContentTypeParticle.
Thanks to #Petru-Gardea

.NET can't deserialize nested structs?

I'm running into issues getting C# (VS2008, Compact Framework, .NET is version 3.5 SP1) to successfully deserialize nested structs. The problem only appears in CF when I'm running on the emulator for the mobile device (I'm using the "Pocket PC 2003 Second Edition" emulator), the exact same code running on my Windows box does not have the same problem.
Here's my code:
public struct Fred
{
public string Name;
}
public struct Middle
{
public Fred[] Freds;
}
public struct Top
{
public Middle Middle;
public Fred[] Freds;
}
public static void Test()
{
Top top = new Top();
top.Middle.Freds = new Fred[2];
top.Middle.Freds[0].Name = "Fred20";
top.Middle.Freds[1].Name = "Fred21";
top.Freds = new Fred[2];
top.Freds[0].Name = "Fred10";
top.Freds[1].Name = "Fred11";
StringBuilder sb = new StringBuilder();
System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(top.GetType());
using (StringWriter sw = new StringWriter(sb))
{
x.Serialize(sw, top);
}
string xml = sb.ToString();
string[] lines = xml.Split(new char[] { '\r', '\n' });
foreach (string line in lines)
{
Debug.WriteLine(" " + line.Trim());
}
MemoryStream ms = new MemoryStream(System.Text.Encoding.ASCII.GetBytes(xml));
StreamReader sr = new StreamReader(ms);
object o = x.Deserialize(sr);
Debug.WriteLine("Deserialized into " + o);
Top go2 = (Top)o;
if (go2.Freds == null)
Debug.WriteLine(" go2.Freds is null");
else
Debug.WriteLine(" go2.Freds[0].Name is \"" + go2.Freds[0].Name + "\"");
if (go2.Middle.Freds == null)
Debug.WriteLine(" go2.Middle.Freds is null");
else
Debug.WriteLine(" go2.Middle.Freds[0].Name is \"" + go2.Middle.Freds[0].Name + "\"");
}
When I run this, the XML it creates looks good:
<?xml version="1.0" encoding="utf-16"?>
<Top xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Middle>
<Freds>
<Fred>
<Name>Fred20</Name>
</Fred>
<Fred>
<Name>Fred21</Name>
</Fred>
</Freds>
</Middle>
<Freds>
<Fred>
<Name>Fred10</Name>
</Fred>
<Fred>
<Name>Fred11</Name>
</Fred>
</Freds>
</Top>
but C# is unable to successfully deserialize this XML - the console output is this:
Deserialized into Top
go2.Freds[0].Name is "Fred10"
go2.Middle.Freds is null
xsd has similar problems:
<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="Top" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xs:element name="Top" msdata:IsDataSet="true" msdata:UseCurrentLocale="true">
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="Middle">
<xs:complexType>
<xs:sequence>
<xs:element name="Freds" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="Fred" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="Name" type="xs:string" minOccurs="0" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:choice>
</xs:complexType>
</xs:element>
</xs:schema>
Have I just encountered a C# bug? Or am I missing something obvious?
Note: It's not a problem with using the name twice, if I create a struct named George that is identical to Fred, and change the contents of Middle to public George[] George, the problem isn't any better.
TLDR (for the skimmers): This post consist of two parts.
Part 1: A quick intro to Protobuf. Attributes are used here.
Part 2: The actual answer to the question: to configure serialization without modifying the inherited library
Ok, I'll give it a shot.
The problem seems to be that you're using the Compact Framework, which does not have the same serialization/deserialization capabilities as the full .NET framework. So we need some custom serialization here.
Going with the philosophy of the Compact Framework, my guess is that you also want something that performs well and has a small footprint. So I picked Protobuf for the task (which is also approximately 12 times faster than XmlSerializer)
You can install it by running this command:
Install-Package protobuf-net
Let's start with the easy way - by adding attributes to your model.
Configuration without attributes comes next, as you pointed out that the original model can't/shouldn't be modified. This is only for illustration.
Decorated with the appropriate attributes, your model would look like this:
Part 1: Configuration with attributes.
I repeat, this part is only for illustration purposes - read on to "Configuration without attributes"
[ProtoContract]
public struct Fred
{
[ProtoMember(1)]
public string Name;
}
[ProtoContract]
public struct Middle
{
[ProtoMember(1)]
public Fred[] Freds;
}
[ProtoContract]
public struct Top
{
[ProtoMember(1)]
public Middle Middle;
[ProtoMember(2)]
public Fred[] Freds;
}
The only thing to note here is the usage of numbered members, called keys. It's essentially the same thing as giving them property names in the case of JSON or XML serialization, except this is the protobuf way to do it. You simply assign a unique integer value to each member within the same class, and most of the times you're done there.
For convenience let's add a simple Builder from which we can instantiate a Top which is similar to the one in your example:
public class TopTestBuilder
{
public Top BuildDefaultTestTop()
{
var top = new Top
{
Middle = new Middle
{
Freds = new[]
{
new Fred {Name = "Fred20"},
new Fred {Name = "Fred21"}
}
},
Freds = new[]
{
new Fred {Name = "Fred10"},
new Fred {Name = "Fred11"}
}
};
return top;
}
}
We can serialize it like this:
Top topIn = new TopTestBuilder().BuildDefaultTestTop();
string serialized;
using (var stream = new MemoryStream())
{
Protobuf.Serializer.Serialize(stream, topIn);
stream.Position = 0;
var reader = new StreamReader(stream);
serialized = reader.ReadToEnd();
}
// Output: "\nDC4\n\b\nACKFred20\n\b\nACKFred21DC2\b\nACKFred10DC2\b\nACKFred11"
And deserialize it like this:
Top topOut;
using (var stream = new MemoryStream())
{
var writer = new StreamWriter(stream);
writer.Write(serialized);
writer.Flush();
stream.Position = 0;
topOut = Protobuf.Serializer.Deserialize<Top>(stream);
}
As you can see there is a little bit of plumbing for the MemoryStreams, but other than that it should look familiar to how other types of serialization work. Similarly, everything can just as well be accomplished by configuring a custom TypeModel, allowing serialization to be fully decoupled from the model.
Part 2: Configuration without attributes
By default, Protobuf uses the attributes to define the TypeModel and then stores it in ProtoBuf.Meta.RuntimeTypeModel.Default. This property is used when you call the static Protobuf.Serializer directly.
We can also define our own. It took some fiddling around (note to self: RTFM) to get it working but it turned out to be almost as simple:
var model = TypeModel.Create();
// The first parameter (maps to ProtoContractAttribute) is the Type to be included.
// The second parameter determines whether to apply default behavior,
// based on the attributes. Since we're not using those, this has no effect.
model.Add(typeof(Fred), false);
model.Add(typeof(Middle), false);
model.Add(typeof(Top), false);
// The newly added MetaTypes can be accessed through their respective Type indices.
// The first parameter is the unique member number, similar to ProtoMemberAttribute.
// The second parameter is the name of the member as it is declared in the class.
// When the member is a list:
// The third parameter is the Type for the items.
// The fourth parameter is the Type for the list itself.
model[typeof(Fred)].Add(1, "Name");
model[typeof(Middle)].Add(1, "Freds", typeof(Fred), typeof(Fred[]));
model[typeof(Top)].Add(1, "Middle");
model[typeof(Top)].Add(2, "Freds", typeof(Fred), typeof(Fred[]));
Now all we have to do is change one line of code for both functions:
Serialize:
Top topIn = new TopTestBuilder().BuildDefaultTestTop();
string serialized;
using (var stream = new MemoryStream())
{
model.Serialize(stream, top);
stream.Position = 0;
var reader = new StreamReader(stream);
serialized = reader.ReadToEnd();
}
Deserialize:
Top topOut;
using (var stream = new MemoryStream())
{
var writer = new StreamWriter(stream);
writer.Write(serialized);
writer.Flush();
stream.Position = 0;
topOut = (Top) _model.Deserialize(stream, null, typeof (Top));
}
And it works just the same. Perhaps add a class to keep things organized - give it two public methods Serialize and Deserialize, and a private method BuildTypeModel (to call from the constructor and store in a field on the serializer?)
Your calling code would end up looking something like this:
var serializer = new CustomProtoBufSerializer();
var serialized = serializer.Serialize(someClassInput);
SomeClass someClassOutput = serializer.Deserialize(serialized);
One thing quickly became clear though - Protobuf hasn't been as thoroughly documented and tested as most JSON and XML serializers out there. This, along with the serialization results being non-readable to humans, could be a drawback in some situations. Other than that it seems like it's fast, lightweight and compatible with many different environments.
The absence of automatic type resolution bothered me a bit, so I went looking and found something that seems pretty interesting: Protobuf T4 TypeModel Generator. I haven't been able to try it yet. If people are interested, I might do that later and update the answer with a more generic solution.
Let me know if you have any trouble getting it to work.

C# Web Service client cast error

I am trying to consume a Java web service but get an exception System.InvalidCastException: Cannot assign object of type ValueArrayType to an object of type ValueArrayType[]
I am consuming a third party service so cannot change the service and have been informed that they can consume the service ok with php and java.
Value Array type is complex type
<xsd:complexType name="ValueArrayType">
<xsd:sequence>
<xsd:element name="ValueName" type="xsd:string"/>
<xsd:element name="ValueType" type="xsd:string"/>
<xsd:element name="ValueValue" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
It is an element in the response DetailsType that can have several occurrences as it has max = unbound and is wrapped by a sequence attribute.
<xsd:complexType name="DetailsType">
<xsd:sequence>
<xsd:element name="Id" type="xsd:int"/>
<xsd:element name="MobileName" type="xsd:string"/>
<xsd:element name="ValueArray" type="tns:ValueArrayType" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
I have tried wsdll.exe and svrcutil.exe to try and generate client code.
the ValueArrayType is defined in the generated code as an array.
public ValueArrayType[] ValueArray
{
get
{
return this.valueArrayField;
}
set
{
this.valueArrayField = value;
}
}
an example of the data coming back is.
....
<Details xsi:type="tns:DetailsType">
<Id xsi:type="xsd:int">9999</Id>
<ValueArray xsi:type="tns:ValueArrayType">
<ValueName xsi:type="xsd:string">Count</ValueName>
<ValueType xsi:type="xsd:string">numeric</ValueType>
<ValueValue xsi:type="xsd:string">11</ValueValue>
</ValueArray>
<ValueArray xsi:type="tns:ValueArrayType">
<ValueName xsi:type="xsd:string">Start</ValueName>
<ValueType xsi:type="xsd:string">numeric</ValueType>
<ValueValue xsi:type="xsd:string">31</ValueValue>
</ValueArray>
<ValueArray xsi:type="tns:ValueArrayType">
<ValueName xsi:type="xsd:string">A1</ValueName>
<ValueType xsi:type="xsd:string">numeric</ValueType>
<ValueValue xsi:type="xsd:string">23</ValueValue>
</ValueArray>
<ValueArray xsi:type="tns:ValueArrayType">
<ValueName xsi:type="xsd:string">A2</ValueName>
<ValueType xsi:type="xsd:string">numeric</ValueType>
<ValueValue xsi:type="xsd:string">0</ValueValue>
</ValueArray>
<ValueArray xsi:type="tns:ValueArrayType">
<ValueName xsi:type="xsd:string">X1</ValueName>
<ValueType xsi:type="xsd:string">numeric</ValueType>
<ValueValue xsi:type="xsd:string">0</ValueValue>
</ValueArray>
.....
If I change the client code to public ValueArrayType ValueArray
instead of an array then the client works but only gets the first ValueArray returned.
Have tried suggestions from http://blogs.msdn.com/b/netcfteam/archive/2007/02/01/why-your-netcf-apps-fail-to-call-some-web-services.aspx.
Update
I have generated a WCF Service with the proxyclass generated from scvutil.
When I consume and check the xml with WCFTestCLient.exe.
An Array type is sent back as
<ValueArray>
<ValueArrayType>
<ValueName>a</ValueName>
<ValueType>string</ValueType>
<ValueValue>1</ValueValue>
</ValueArrayType>
<ValueArrayType>
<ValueName>a</ValueName>
<ValueType>string</ValueType>
<ValueValue>2</ValueValue>
</ValueArrayType>
</ValueArray>
I assume either the data being sent does not match the WSDL or there is a bug in the C# scvutil, or System.ServiceModel.
Try to specify your element type inside the generated code
[XmlElement(ElementName = "ValueArray", Type = typeof(ValueArrayType), Namespace = "YourSchemaNamespace")]
public ValueArrayType[] ValueArray
{
get
{
return this.valueArrayField;
}
set
{
this.valueArrayField = value;
}
}
More information is available at MSDN
The problem is caused by incorrect xsi:type values that mislead WCF deserialization (described here).
The workaround is to use OperationFormatUse.Literal instead of OperationFormatUse.Encoded on all operations.
Can you try something like this ?
JavaServecie js= new JavaService();
js.ValueArrayType arr= js.GetValues(.....
if the class is public.

Detailed ServiceDescription / Proxy from WSDL

I am using the classes ServiceDescription / ServiceDescriptionImporter to dynamically call web services. I'd like to dig a bit deeper into the WSDL description and get
1) Parameter info for each of the web methods
2) The actual types / composition each parameters of all the web methods (ie if a WebMethod takes some complex type as a parameter, I need to know the primitive / other types it is composed of as well, if possible)
Here is the code I have for the dynamical call:
public static object CallWebService(string webServiceAsmx, string serviceName, string methodName, object[] args = null)
{
WebClient client = new WebClient();
Stream stream = client.OpenRead(webServiceAsmx + "?wsdl");
ServiceDescription description = ServiceDescription.Read(stream);
ServiceDescriptionImporter importer = new ServiceDescriptionImporter();
importer.ProtocolName = "Soap12";
importer.AddServiceDescription(description, null, null);
importer.Style = ServiceDescriptionImportStyle.Client;
importer.CodeGenerationOptions = CodeGenerationOptions.GenerateProperties;
I have been able to get as far as finding some basic info such as the method names, parameter info, but I need deeper analysis. For example I need access to essentially all the information that Wsdl.exe produces in proxy classes but I dont want to have to run Wsdl.exe, just discover the information dynamically. For every method I need to know what its return type is composed of, what its parameters are composed of, etc. I know its in the WSDL just not sure how to programmatically extract it. Here are some of the classes Ive been exploring:
ServiceDescription.Description
ServiceDescription.Messages
ServiceDescription.Types
It seems that alot of them come up empty though?
Thanks in advance.
EDITS
I got a little further, this is the xml schema (WSDL) I am trying to traverse:
- <s:complexType name="Session">
- <s:complexContent mixed="false">
- <s:extension base="tns:EntityObject">
- <s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="Name" type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="Host" type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="SessionType" type="s:string" />
<s:element minOccurs="1" maxOccurs="1" name="LoginTime" type="s:dateTime" />
<s:element minOccurs="1" maxOccurs="1" name="LogoutTime" type="s:dateTime" />
</s:sequence>
</s:extension>
</s:complexContent>
</s:complexType>
And this is the code to traverse:
foreach (System.Xml.Schema.XmlSchemaComplexType item in xmlSchema.SchemaTypes.Values)
{
if (item != null)
{
System.Xml.Schema.XmlSchemaParticle particle = item.Particle;
System.Xml.Schema.XmlSchemaSequence sequence = particle as System.Xml.Schema.XmlSchemaSequence;
if (sequence != null)
{
foreach (System.Xml.Schema.XmlSchemaElement childElement in sequence.Items)
{
string name = childElement.Name;
string type = childElement.SchemaTypeName.Name;
Console.WriteLine(name + " " + type);
}
}
}
This gets me a little further, but not as far as getting the complexContent of the ComplexType. Produces the following output in the console:
Session Session
If anyone has a similar question, here is how I did it:
foreach (System.Xml.Schema.XmlSchemaComplexType item in xmlSchema.SchemaTypes.Values)
{
ComplexType cType = new ComplexType(item.Name);
System.Xml.Schema.XmlSchemaContentModel model = item.ContentModel;
System.Xml.Schema.XmlSchemaComplexContent complex = model as System.Xml.Schema.XmlSchemaComplexContent;
if (complex != null)
{
System.Xml.Schema.XmlSchemaComplexContentExtension extension = complex.Content as System.Xml.Schema.XmlSchemaComplexContentExtension;
System.Xml.Schema.XmlSchemaParticle particle = extension.Particle;
System.Xml.Schema.XmlSchemaSequence sequence = particle as System.Xml.Schema.XmlSchemaSequence;
if (sequence != null)
{
List<SimpleType> primitives = new List<SimpleType>();
foreach (System.Xml.Schema.XmlSchemaElement childElement in sequence.Items)
{
string name = childElement.Name;
string type = childElement.SchemaTypeName.Name;
cType.Primitives.Add(new SimpleType(name, type));
}
if (cType.Name == parameter.Type || "ArrayOf" + cType.Name == parameter.Type)
{
descriptions.Add(new ComplexParameter(cType, item.Name));
}
}
}
}
This looks pretty similar to what you you did WebServiceInfo It returns custom objects with the info. (if I am understanding your question and the blog well enough)

XmlSerializer Deserialize failures

I have wsdl from third party server. Ran svcutil and ended up wih a set of
XmlNode AMethod(object Request);
methods. There is a separate 100 page pdf describing response/request objects for each method
My thought was wrap web methods and use XmlSerializer to return strongly typed objects. Returned xml looks like this (i removed soap headers):
<Response xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="ResponseExt"
xmlns="http://www.thirdparty.com/lr/">
<Code>0</Code>
<Message>SUCCESS</Message>
<SessionId>session_token</SessionId>
</Response>
Looked simple. Created a class(from document/wire captures):
[XmlRoot("Response")]
//EDIT added XmlType
[XmlType("ResponseExt", Namespace = "http://www.thirdparty.com/lr/")]
public class MyClass {
public string Code {get; set;}
public string Message {get; set;}
public string SessionId {get; set;}
}
Processing time:
//XmlNode node = xml from above
XmlSerializer serializer = new XmlSerializer(typeof(MyClass));
XmlNodeReader reader = new XmlNodeReader(node);
Myclass myclass = serializer.Deserialize(reader) as MyClass
Last line is where it blows up with inner exception message: The specified type was not recognized: name='ResponseExt', namespace='http://www.thirdparty.com/lr/', at <Response xmlns=''>.
I can't figure out how to make Serializer happy and what exactly these two mean
xsi:type="ResponseExt"
xmlns="http://www.thirdparty.com/lr/
As always any advice and pointer are appreciated
EDIT: Accepted answer below.
I was still getting exception, until i found this, hopefully it'll save someone some time.
I started to work backwards. Captured xml on the wire. Deserialized to my created classes with correct attributes: worked like a charm. Tried again from webservice - exception. For some reason XmlSerializer doesn't recognize ResponseExt.
XmlSerializer serializer = new XmlSerializer(typeof(Response));
XmlNode node = (XmlNode)results[0];
XmlDocument doc = new XmlDocument();
doc.LoadXml(node.OuterXml); //reload node
XmlNodeReader reader = new XmlNodeReader(doc.FirstChild); //there is only one node
Response rsp = serializer.Deserialize(reader) as Response; //works
EDIT: underlying issue wsdl file was not complete. After spending 2 days on this and finding this (ugly) workaround, third-party vendor provided complete WSDL with all types that deserialize without errors.
Why are you manually deserializing XML, when you have WSDL ?
If you have WSDL, use the svcutil.exe tool, or the wsdl.exe tool, to generate proxy classes and DTOs for the XML messages being sent and received on the wire.
The point of a web services toolkit, or "stack" is to provide this for you, so that you don't have to author classes and XML serialization code by hand.
Did you try this? Did you try to run the WSDL through one of those tools? Or did you try to "Add web reference" in Visual Studio?
After updating the question, I suggest that you modify the WSDL, rather than write custom code. You can produce a custom WSDL for the service, which will correctly generate the proxy classes you want. If you don't need all 100 methods (or however many there are), then leave them out. If you want a custom object from a method, then define a complexType that corresponds to that object. This is much simpler and more reliable than hand-authoring XML deserialization code for each method.
If you don't like that idea, and want to stick with manually writin the XML deserialization code, then you need to do two things:
attach a namespace to the XmlRoot attribute.
change the name of your class to ResponseExt, and derive it from a class called Response. Decorate that Response class with an XmlInclude attribute. This aligns the use of the Xml Serializer with the xsi:type used in the XML fragment.
It looks like this in code:
[XmlRoot("Response", Namespace="http://www.thirdparty.com/lr/")]
public class ResponseExt : Response {
}
[XmlRoot("Response", Namespace="http://www.thirdparty.com/lr/")]
[XmlInclude(typeof(ResponseExt))]
public class Response {
public string Code {get; set;}
public string Message {get; set;}
public string SessionId {get; set;}
}
public class XsiType
{
public static void Main(string[] args)
{
try
{
string filename = "XsiType.xml";
XmlSerializer s1 = new XmlSerializer(typeof(Response));
ResponseExt r = null;
using(System.IO.StreamReader reader= System.IO.File.OpenText(filename))
{
r= (ResponseExt) s1.Deserialize(reader);
}
var builder = new System.Text.StringBuilder();
var xmlws = new System.Xml.XmlWriterSettings { OmitXmlDeclaration = true, Indent= true };
using ( var writer = System.Xml.XmlWriter.Create(builder, xmlws))
{
//s1.Serialize(writer, r, ns);
s1.Serialize(writer, r);
}
string xml = builder.ToString();
System.Console.WriteLine(xml);
}
catch (System.Exception exc1)
{
Console.WriteLine("Exception: {0}", exc1.ToString());
}
}
}
related: How can I force the use of an xsi:type attribute?

Categories