XmlSerializer Deserialize failures - c#

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?

Related

Kafka Producer Error: ' Value serializer not specified and there is no default serializer defined for type ...'

I just started using Kafka and hit the following rookie error:
'Value cannot be null.
Parameter name: Value serializer not specified and there is no default serializer defined for type ActMessage.'
It happens when trying to send a class object, ActMessage object, rather then the a simple string that comes with the example. The line of code that raises the erros is:
using (var p = new ProducerBuilder<Null, ActMessage>(config ).Build()
I am using the .net client.
My understanding is that i need to use one of the default serializes in the first type parameter, one that come with Kafka client, as explained here, but can't find them on this .net package.
I guess i could build one but that would be a waste of time.
Here a reproducible example:
public class ActMessage {
public int SomeId {get;set;}
public string SomeContent {get;set;}
}
class Tester {
void send(){
var config = new ProducerConfig { BootstrapServers = "localhost:9092" };
using (var p = new ProducerBuilder<Null, ActMessage>(config).Build()) //throws error here
{
var dr = p.ProduceAsync("news", new Message<Null, ActMessage>
{
Value = new ActMessage { SomeId = 1, SomeContent="hi" },
}
).Result;
}
}
}
I suggest checking out the working examples/ dir in that repo to see working code that you can copy into your own projects.
If you have your own class, you need to implement the ISerializer and IDeserializer interfaces.
Or you can use the built-in ones
However, an alternative is to use Avro
This requires writing an Avro schema file, then using avrogen to create your class, not manually write it. E.g.
dotnet tool install --global Apache.Avro.Tools
avrogen -s User.avsc .
Then you must always add some ValueSerializer in Kafka clients in order to send data

Returning XML from web API c#

newbie here as long as web API is concerned.
web Api:
[HttpGet]
public IHttpActionResult Test()
{
var doc = new XmlDocument();
XmlElement tournament = (XmlElement)doc.AppendChild(doc.CreateElement("Tournament"));
XmlElement match = (XmlElement)tournament.AppendChild(doc.CreateElement("Match"));
match.SetAttribute("ID", "SomeMatch");
return Ok(doc.InnerXml);
}
Result:
<string xmlns="http://schemas.microsoft.com/2003/10/Serialization/"><Tournament><Match ID="SomeMatch" /></Tournament></string>
Two problems:
why it is wrapped in this string element when my XML does not have it?
why < is converted to "& lt;" and > to "& gt;"
and how to get back just
<Tournament>
<Match ID="SomeMatch" /></Tournament>
Since you are returning a string from the action, ASP.Net Web API is creating the equivalent XML to represent a string.
Now, if you were to ensure that API uses XML serialization, client can add accept header to the request. Alternatively, you can specify the formatter either the way you have or in the action itself by returning Content using following constructor:
return Content (HttpStatusCodeHere, doc.DocumentElement, Configuration.Formatters.XmlFormatter)
Note that I do not have access to Visual Studio so class/property names might not be accurate.
You have to returns doc.DocumentElement instead of doc.InnerXml.
Because doc.InnerXml gives you xml in string format that's why your xml is shown in <string> format
[HttpGet]
public IHttpActionResult Test()
{
var doc = new XmlDocument();
XmlElement tournament = (XmlElement)doc.AppendChild(doc.CreateElement("Tournament"));
XmlElement match = (XmlElement)tournament.AppendChild(doc.CreateElement("Match"));
match.SetAttribute("ID", "SomeMatch");
return Ok(doc.DocumentElement);
}
All your api(s) returns output is in xml format because your HttpConfiguration set in XmlFormatter by default.

Why can't I see my XML documentation when decompiling using ICSharpCode.Decompiler?

I've been looking into decompiling .dlls using ICSharpCode.Decompiler and found some sample code and fingers in the right direction on this thread:
https://social.msdn.microsoft.com/Forums/vstudio/en-US/00e59445-9f85-4ec5-a04f-9796a72a00a2/library-to-decompile-assembly-to-c
I've copied the code and added set the variable ShowXmlDocumentation = true in the DecompilerSettings, however I'm still unable to see my documentation.
My source code looks like this:
var settings = new DecompilerSettings
{
FullyQualifyAmbiguousTypeNames = true,
ShowXmlDocumentation = true
};
const string assemblyName = "Experiments.Decompilation.dll";
var assembly1 = AssemblyDefinition.ReadAssembly(assemblyName);
var decompilerContext = new DecompilerContext(assembly1.MainModule) {Settings = settings};
var decompiler = new AstBuilder(decompilerContext);
decompiler.AddAssembly(assembly1);
var output = new StringWriter();
decompiler.GenerateCode(new PlainTextOutput(output));
var byteArray = Encoding.ASCII.GetBytes(output.ToString());
TextReader codeReader = new StreamReader(new MemoryStream(byteArray));
var line = codeReader.ReadToEnd();
Yet the variable line never has any of the expected XML documentation in it.
I get the full trace of what's in the .dll, for example
namespace Experiments.Decompilation
{
public interface ITest
{
long Method();
}
}
But I was expecting the interface method to have the XML docs I defined, a la
namespace Experiments.Decompilation
{
///<summary>
///This is some test documentation
///</summary>
public interface ITest
{
long Method();
}
}
But no cookie for me.
Am I missing anything? Do I need to change any other configuration?
If anyone has any ideas on this I'd really appreciate it. I've been wracking my brains and haven't found a solution myself, so here you are SO, please help!
Because that property doesn't actually do anything within the ICSharpCode.Decompiler library. It's just there to support projects (like ILSpy) that want to consume the decompiler.
ILSpy, for example, will check to see if the decompiler has the option set; if so, it will look up the appropriate XML file on-disk and parse the XMLDoc strings, and embed them in the final output.
Also, note that the actual .NET assembly doesn't have the XMLDoc in it. Visual Studio generates a separate file with that stuff, and if you don't have that, ILSpy won't be able to include XMLDoc even if you ask.

Convert Child Class to Base Class for XML Serialization?

I have a base class StandardMeasurement and a derived class CustomMeasurement both of which can be serialized.
Here is what I want to do:
Load a CustomMeasurement from file to customMeasInstance.
Create new std. meas. s.t.: StandardMeasurement stdMeas = (StandardMeasurement)customMeasInstance.
Serialize stdMeas (only) as a StandardMeasurement type.
I get an error when trying because stdMeas is still considered a CustomMeasurement by the XML serializer. Is there a way I can do this or do I have to "copy" over all the info manually?
Thanks!
Using the System.Xml.Serialization.XmlSerializer class, you should be able to do something like this:
EDIT - Removed object initializer for compatibility with .NET 2.0
var cust = new CustomMeasurement();
cust.SomeProperty = "Foo";
cust.AnotherProperty = "Bar";
var serializer = new XmlSerializer(typeof(StandardMeasurement), new Type[] { cust.GetType() });
serializer.Serialize(Console.Out, cust);

How can I find the position where a string is malformed XML (in C#)?

I'm writing a lightweight XML editor, and in cases where the user's input is not well formed, I would like to indicate to the user where the problem is, or at least where the first problem is. Does anyone know of an existing algorithm for this? If looking at code helps, if I could fill in the FindIndexOfInvalidXml method (or something like it), this would answer my question.
using System;
namespace TempConsoleApp
{
class Program
{
static void Main(string[] args)
{
string text = "<?xml version=\"1.0\"?><tag1><tag2>Some text.</taagg2></tag1>";
int index = FindIndexOfInvalidXml(text);
Console.WriteLine(index);
}
private static int FindIndexOfInvalidXml(string theString)
{
int index = -1;
//Some logic
return index;
}
}
}
I'd probably just cheat. :) This will get you a line number and position:
string s = "<?xml version=\"1.0\"?><tag1><tag2>Some text.</taagg2></tag1>";
System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
try
{
doc.LoadXml(s);
}
catch(System.Xml.XmlException ex)
{
MessageBox.Show(ex.LineNumber.ToString());
MessageBox.Show(ex.LinePosition.ToString());
}
Unless this is an academic exercise, I think that writing your own XML parser is probably not the best way to go about this. I would probably check out the XmlDocument class within the System.Xml namespace and try/catch exceptions for the Load() or LoadXml() methods. The exception's message property should contain info on where the error occurred (row/col numbers) and I suspect it'd be easier to use a regular expression to extract those error messages and the related positional info.
You should be able to simply load the string into an XmlDocument or an XmlReader and catch XmlException. The XmlException class has a LineNumber property and a LinePosition property.
You can also use XmlValidatingReader if you want to validate against a schema in addition to checking that a document is well-formed.
You'd want to load the string into an XmlDocument object via the load method and then catch any exceptions.
public bool isValidXml(string xml)
{
System.Xml.XmlDocument xDoc = null;
bool valid = false;
try
{
xDoc = new System.Xml.XmlDocument();
xDoc.loadXml(xmlString);
valid = true;
}
catch
{
// trap for errors
}
return valid;
}

Categories