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 PaymentNotificationResponse
and 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.