Serialize to XML with SoapEnvelope in C# - c#

I'm not very experienced with SOAP calls, so I'm struggling to get my requests to format correctly. I am creating an instance of my object (using the .wsdl file that was supplied) then serialize it using the below code. However, the object is not wrapped in a soap envelope tag which is causing my request to error. Does anyone know how I can modify this to wrap my object correctly?
public static string SerializeToXml<T>(T dataToSerialize)
{
if (dataToSerialize == null) return null;
using (StringWriter stringWriter = new StringWriter())
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
serializer.Serialize(stringWriter, dataToSerialize);
return stringWriter.ToString();
}
}

Related

How to remove xmlns:xsi and xmlns:xsd from Web API XML serializer

For some hours, I have battled with removing the default namespaces from the XML returned from serializing my independent objects (not MVC model) in ASP.NET Web Api. Sample codes for the application is:
Class Definition:
public class PaymentNotificationResponse
{
[XmlArray("Payments")]
[XmlArrayItem("Payment", typeof(PaymentResponse))]
public PaymentResponse[] Payments { get; set; }
}
I then created a Web Api Controler that creates an object of the PaymentNotificationResponse based on some input, and then serialize the object to the requesting party. The Controller is listed below:
public class PaymentController : ApiController
{
public PaymentNotificationResponse Post()
{
//Read request content (only available async), run the task to completion and pick the stream.
var strmTask = Request.Content.ReadAsStreamAsync();
while (strmTask.Status != TaskStatus.RanToCompletion) { }
Stream strm = strmTask.Result;
//Go back to the beginning of the stream, so that the data can be retrieved. Web Api reads it to the end.
if (strm.CanSeek)
strm.Seek(0, SeekOrigin.Begin);
//Read stream content and convert to string.
byte[] arr = new byte[strm.Length];
strm.Read(arr, 0, arr.Length);
String str = Encoding.UTF8.GetString(arr);
//Change the default serializer to XmlSerializer from DataContractSerializer, so that I don't get funny namespaces in properties.
//Then set a new XmlSerializer for the object
Configuration.Formatters.XmlFormatter.UseXmlSerializer = true;
Configuration.Formatters.XmlFormatter.SetSerializer<PaymentNotificationResponse>(new XmlSerializer(typeof(PaymentNotificationResponse)));
//Now call a function that would convert the string to the required object, which would then be serialized when the Web Api is invoked.
return CreatePaymentNotificationFromString(str);
}
}
Problem is, when I invoke the Api with valid string parameter, it returns an XML of this format (valid XML, but xmlns is not needed):
<PaymentNotificationResponse xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Payments>
<Payment>
<PaymentLogId>8325</PaymentLogId>
<Status>0</Status>
</Payment>
</Payments>
</PaymentNotificationResponse>
The system I'm sending this to doesn't need the xmlns:xsi and the xmlns:xsd. In fact, it returns an exception when it sees the namespaces.
I tried returning a string with XML tags, it just wrapped the response in a <string></string> and encoded all the < and >. So that was not an option.
I saw this post and this one. While the former is very detailed, it didn't solve my problem. It only just introduced one extra xmlns to the generated XML. I think it's because I didn't explicitly call .Serialize() function in the XmlSerializer.
I figured out a solution, and I thought I should share. So I would state it in the answers.
To fix this, I added a method to serialize the PaymentNotificationResponseand return an XML string, thus (I included this in the definition of PaymentNotificationResponse):
//I added this method after I tried serialize directly to no avail
public String SerializeToXml()
{
MemoryStream ms = new MemoryStream();
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
new XmlSerializer(typeof(PaymentNotificationResponse)).Serialize(ms, this, ns);
XmlTextWriter textWriter = new XmlTextWriter(ms, Encoding.UTF8);
ms = (System.IO.MemoryStream)textWriter.BaseStream;
return new UTF8Encoding().GetString(ms.ToArray());
}
I parsed the string to create an XDocument, then return the root element. This made me change the return type of the Post() method to XElement. The Post() listing would then be:
public XElement Post()
{
//...Same as it was in the question, just the last line that changed.
var pnr = CreatePaymentNotificationFromString(str);
return XDocument.Parse(pnr.SerializeToXml()).Root;
}
That would make my response XML this:
<PaymentNotificationResponse>
<Payments>
<Payment>
<PaymentLogId>8325</PaymentLogId>
<Status>0</Status>
</Payment>
</Payments>
</PaymentNotificationResponse>
I hope this helps someone.

Deserialization with XmlSerializer

I'm writing a Windows Store App and I need to store some data in Roaming Storage; for that I needed to serialize/deserialize some data. My serializer/deserializer functions are as below:
public string Serialize()
{
try
{
StringWriter sw = new StringWriter();
XmlSerializer xs = new XmlSerializer(typeof(RssData));
xs.Serialize(sw, this);
return sw.ToString();
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex);
return String.Empty;
}
}
public void Deserialize(string serialized)
{
try
{
XmlSerializer xs = new XmlSerializer(typeof(RssData));
XmlReader xr = XmlReader.Create(new StringReader(serialized));
RssData rd = xs.Deserialize(xr) as RssData;
this.categoryList = rd.categoryList;
this.defaultCategory = rd.defaultCategory;
}
catch(Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex);
}
}
In summary, RssData has a list of RssCategory, and each RssCategory has a RssFeed. There is no problem with serialization and the following string is an example that I've produced using the Serialize function above:
<?xml version="1.0" encoding="utf-16"?>
<RssData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<CategoryList>
<RssCategory>
<Name>No Category</Name>
<RssList>
<RssFeed>
<Title>HABERTURK.COM</Title>
<Url>http://www.haberturk.com/rss</Url>
<Image>http://www.haberturk.com/images/htklogo2.jpg</Image>
<Subtitle>HABERTÜRK - Türkiye'nin En Büyük İnternet Gazetesi</Subtitle>
</RssFeed>
</RssList>
</RssCategory>
</CategoryList>
</RssData>
However, when I Deserialize this XML, the resulting RssData has a CategoryList with 2 RssCategories, one is a RssCategory named "No Category" with an empty RssList, the other one is the correct one as defined in the XML; i.e. "No Category" with one RssFeed.
Is it a problem with my code or is XmlSerializer bugged?
Ok it seems that XmlSerializer first constructs the object using its no-parameter constructor then adds each item in the XML to the appropriate list. Thus if the constructor adds some items to that list, deserializing will result in a list that includes any item added in the constructor.
Edit: So if you serialize/deserialize a class that has a property of type Array/List/Container etc, in the no-parameter constructor you have to initialize it, but do not add anything to it.
Try this out and tell me:
Class c = new Class(); //class name of data which was serialized
using (var sr = new StringReader(Serialized data, wherever you are fetching it from)
{
var xs = new XmlSerializer(typeof(Class));
c= (Class)xs.Deserialize(sr);
}

Why is XmlSerializer's Deserialize() spitting out a child object which is a XmlNode[]?

I'm using XmlSerializer to serialize and then deserialize a simple object. When I deserialize the object to my surprise I find a child object was not properly deserialized but instead turned into XmlNode[].
Here is very nearly the structure I've got:
// This line I put in here as a way of sneaking into the XML the
// root node's C# namespace, since it's not the same as the
// deserializing code and the deserializing code seemed unable to
// deserialize properly without knowing the Type (see my code below).
// So I basically just use this fake construct to get the namespace
// and make a Type of it to feed the XmlSerializer() instantiation.
[XmlRoot(Namespace = "http://foo.com/CSharpNamespace/Foo.Bar")]
// This is because QueuedFile can be given to the Argument array.
[XmlInclude(typeof(QueuedFile))]
// This class is Foo.Bar.CommandAndArguments
public class CommandAndArguments {
public String Command;
public object[] Arguments;
}
// I don't think this matters to XmlSerialize, but just in case...
[Serializable()]
// I added this line just thinking maybe it would help, but it doesn't
// do anything. I tried it without the XmlType first, and that
// didn't work.
[XmlType("Foo.Baz.Bat.QueuedFile")]
// This class is Foo.Baz.Bat.QueuedFile (in a different c#
// namespace than CommandAndArguments and the deserializing code)
public QueuedFile {
public String FileName;
public String DirectoryName;
}
And the code which deserializes it looks like:
public static object DeserializeXml(String objectToDeserialize)
{
String rootNodeName = "";
String rootNodeNamespace = "";
using (XmlReader xmlReader = XmlReader.Create(new StringReader(objectToDeserialize)))
{
if (xmlReader.MoveToContent() == XmlNodeType.Element)
{
rootNodeName = xmlReader.Name;
rootNodeNamespace = xmlReader.NamespaceURI;
if (rootNodeNamespace.StartsWith("http://foo.com/CSharpNamespace/"))
{
rootNodeName = rootNodeNamespace.Substring("http://foo.com/CSharpNamespace/".Length) + "." +
rootNodeName;
}
}
}
//MessageBox.Show(rootNodeName);
try
{
Type t = DetermineTypeFromName(rootNodeName);
if (t == null)
{
throw new Exception("Could not determine type of serialized string. Type listed as: "+rootNodeName);
}
var s = new XmlSerializer(t);
return s.Deserialize(new StringReader(objectToDeserialize));
// object o = new object();
// MethodInfo castMethod = o.GetType().GetMethod("Cast").MakeGenericMethod(t);
// return castMethod.Invoke(null, new object[] { s.Deserialize(new StringReader(objectToDeserialize)) });
}
catch (InvalidOperationException)
{
return null;
}
}
And here is the XML when the CommandAndArguments is serialized:
<?xml version="1.0" encoding="utf-16"?>
<CommandAndArguments xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://foo.com/CSharpNamespace/Foo.Bar">
<Command>I am a command</Command>
<Arguments>
<anyType xsi:type="Foo.Baz.Bat.QueuedFile">
<FileName xmlns="">HelloWorld.txt</FileName>
<DirectoryName xmlns="">C:\foo\bar</DirectoryName>
</anyType>
</Arguments>
</CommandAndArguments>
But when I deserialize I am given a CommandAndArguments object where Arguments is XmlNode[] with the first item being the attribute giving the QueuedFile as the type and the other indices being elements of the properties. But why wasn't the QueuedFile object recreated?
I suspect this might somehow have do with C# namespaces and the engine doing the deserializing not being able to find or work with QueuedFile... But I don't see why since when I forgot the XmlInclude() it made sure to tell me it didn't expect QueuedFile and now that I've added the XmlInclude() I get no error, just an incomplete deserialization.
Help? I've read everything I can find to read and Googled everything I know to Google and am stuck. I certainly have a lot to learn about XML serialization but I'm not sure how I'm failing at something which should be pretty simple (I actually did something almost exactly like this before without any problem, the only difference then was that everything was in the same C# namespace).
Are you able to change the XML format or is it fixed? I don't know what the problem you are having is, but I use the DataContractSerializer classes extensively with no problems.
http://msdn.microsoft.com/en-us/library/system.runtime.serialization.datacontractserializer.aspx
public static void WriteObject(string fileName)
{
Console.WriteLine(
"Creating a Person object and serializing it.");
Person p1 = new Person("Zighetti", "Barbara", 101);
FileStream writer = new FileStream(fileName, FileMode.Create);
DataContractSerializer ser =
new DataContractSerializer(typeof(Person));
ser.WriteObject(writer, p1);
writer.Close();
}
public static void ReadObject(string fileName)
{
Console.WriteLine("Deserializing an instance of the object.");
FileStream fs = new FileStream(fileName,
FileMode.Open);
XmlDictionaryReader reader =
XmlDictionaryReader.CreateTextReader(fs, new XmlDictionaryReaderQuotas());
DataContractSerializer ser = new DataContractSerializer(typeof(Person));
// Deserialize the data and read it from the instance.
Person deserializedPerson =
(Person)ser.ReadObject(reader, true);
reader.Close();
fs.Close();
Console.WriteLine(String.Format("{0} {1}, ID: {2}",
deserializedPerson.FirstName, deserializedPerson.LastName,
deserializedPerson.ID));
}
To anyone coming along with a similar problem, depending on your situation you're probably better off with NetDataContractSerializer. It is an alternative to DataContractSerializer which records the .Net types in the XML making deserialization a breeze, since it knows exactly what types are involved and thus you do not need to tell it what type the root object is with the deserialize command. And it can produce output in XML or binary form (I prefer XML for easier debugging).
Here is some sample code for easily serializing and deserializing an object to and from a string:
private static object Deserialize(string xml)
{
object toReturn = null;
using (Stream stream = new MemoryStream())
{
byte[] data = System.Text.Encoding.UTF8.GetBytes(xml);
stream.Write(data, 0, data.Length);
stream.Position = 0;
var netDataContractSerializer = new NetDataContractSerializer();
toReturn = netDataContractSerializer.Deserialize(stream);
}
return toReturn;
}
private static string Serialize(object obj)
{
using (var memoryStream = new MemoryStream())
using (var reader = new StreamReader(memoryStream))
{
var netDataContractSerializer = new NetDataContractSerializer();
netDataContractSerializer.Serialize(memoryStream, obj);
memoryStream.Position = 0;
return reader.ReadToEnd();
}
}
Easy as pie!

XslCompiledTransform and Serialization

I am trying to implement some functions that will convert one object to another with XslCompiledTransform.
I found some implementations for Serializing an object to XML string and DeSerialize the XML string to an object.
Another function does the XslCompiledTransform from object1 to obejbct2.
To generate the XSLT file i used the Altova MapForce, just loaded the XML of the serialized objects and mapped some attributes.
Now for the problems:
first I noticed that the XslCompiledTransform doesn't work with XSLT version 2.0. is there any newer functions that do work with XSLT 2.0? maybe some settings?
secondly I get an exception when trying to DeSerialize the XML to an object:
"There was an error deserializing the object of type myObject Input string was not in a correct format."
I don't understand where is the problem.
Does anybody have a sample code that does such a thing? all I find in google are Transformations of HTML code and not objects.
Here are the functions:
private static string runXSLT(string xsltFile, string inputXML)
{
XmlDocument XmlDoc = new XmlDocument();
// Load the style sheet.
XslCompiledTransform xslt = new XslCompiledTransform(true);
xslt.Load(xsltFile);
StringReader StrReader = new StringReader(inputXML);
XmlTextReader XmlReader = new XmlTextReader(StrReader);
//Create an XmlTextWriter which outputs to memory stream
Stream stream = new MemoryStream();
XmlWriter writer = new XmlTextWriter(stream, Encoding.UTF8);
// Execute the transform and output the results to a file.
xslt.Transform(XmlReader, writer);
stream.Position = 0;
XmlDoc.Load(stream);
return XmlDoc.InnerXml;
}
public static string SerializeAnObject(object AnObject)
{
XmlDocument XmlDoc = new XmlDocument();
DataContractSerializer xmlDataContractSerializer = new DataContractSerializer(AnObject.GetType());
MemoryStream MemStream = new MemoryStream();
try
{
xmlDataContractSerializer.WriteObject(MemStream, AnObject);
MemStream.Position = 0;
XmlDoc.Load(MemStream);
return XmlDoc.InnerXml;
}
finally
{
MemStream.Close();
}
}
public static Object DeSerializeAnObject(string XmlOfAnObject, Type ObjectType)
{
StringReader StrReader = new StringReader(XmlOfAnObject);
DataContractSerializer xmlDataContractSerializer = new DataContractSerializer(ObjectType);
XmlTextReader XmlReader = new XmlTextReader(StrReader);
try
{
Object AnObject = xmlDataContractSerializer.ReadObject(XmlReader);
return AnObject;
}
finally
{
XmlReader.Close();
StrReader.Close();
}
}
Thanks allot,
Omri.
XslCompiledTransform does not support XSLT 2.0. In fact, XSLT 2.0 is not supported within the .NET Framework at all (you could try the Saxon version for .NET, but be aware that this is just the Java version running inside IKVM).
From your description I did not understand why you are taking the detour via XML to convert one object into another. Why don't you simply provide a constructor in your target object that takes your input object as a paramater? Then you can code all the mapping inside that constructor. This is not onlyby far more efficient than serializing, transforming and deserializing your objects you will also get the type safety of C#.

How to stop XmlSerializer from adding newlines to blank elements

I am serializing an object like this:
XmlSerializer serializer = new XmlSerializer(obj.GetType());
using (StringWriter writer = new StringWriter())
{
serializer.Serialize(writer, obj);
return writer.ToString();
}
(having created the nodes like this)
XmlElement newchild = doc.CreateElement(nodename);
newchild.InnerText = data;
targetnode.AppendChild(newchild);
if data!="" all is well and serializer returns:
<mynode>TheData</mynode>
If data=="" the serializer returns:
<mynode>
</mynode>
Where did that blank line come from?
I've tried the obvious like only setting newchild.InnerText=data when data is nonblank.
In XML both <mynode><\mynode> and <mynode>\n</mynode> are equivalent, so it should not matter, but you could modify the underlining XMLWriter to Serialize the output the way you want it.
Found a simple route
if (data.Length == 0) newchild.IsEmpty = true;
else newchild.InnerText = data;
hope this helps someone.

Categories

Resources