I have a WebApi that returns a simple object, but when I'm forcing it to return as XML (Accept: application/xml) it ignores the [XmlAttribute] attribute I've set on the object.
This is my object:
public class Foo
{
[XmlAttribute]
public string Bar { get; set; }
}
And I return it like this in the code:
[RoutePrefix("api/mytest")]
public class MyTestController : System.Web.Http.ApiController
{
[HttpGet]
[Route("gettest")]
public Foo GetTest()
{
return new Foo() { Bar = "foobar" };
}
}
The resulting XML is:
<Foo>
<Bar>foobar</Bar>
</Foo>
Whereas I would expect it to be returned like this:
<Foo Bar="foobar">
</Foo>
Why does the XmlSerializer used by WebApi ignore the [XmlAttribute] attribute, and how can I make it work like I want to?
Try setting this global configuration value in your WebApi to true
GlobalConfiguration.Configuration.Formatters.XmlFormatter.UseXmlSerializer = true;
By default Web API uses DataContractSerializer in XmlMediaTypeFormatter.
This worked for me... no need to change global configuration.
var configuration = new HttpConfiguration();
configuration.Formatters.XmlFormatter.UseXmlSerializer = true;
return Request.CreateResponse(HttpStatusCode.OK, myObjectToSerialize, configuration);
Related
I have an List<AnimalsEnum> Foo property in a class that I'm serializing to XML with RestSharp for the body of a request. I'd like the output to be:
<rootNode>
... existing content...
<Foo>Elephant</Foo>
<Foo>Tiger</Foo>
.... more content
Instead, for the relevant serialisation part, I have
<Foo>
<AnimalsEnum />
<AnimalsEnum />
</Foo>
I'd like to convert the enum values to strings and remove the container element that is automatically added. Is this possible with RestSharp? I thought it may be possible with attributes, but apparently not. Am I going to have to wrangle this output myself with a custom serialiser?
Code is difficult to post, but keeping with the example:
class Bar
{
public string Name{get;set;}
public List<AnimalsEnum> Foo{get;set;}
public enum AnimalsEnum {Tiger,Elephant,Monkey}
}
and to serialize into a request
var req = new RestSharp.RestRequest(RestSharp.Method.POST);
req.RequestFormat = RestSharp.DataFormat.Xml;
req.AddQueryParameter("REST-PAYLOAD", "");
req.AddXmlBody(myBar);
You can use the built-in DotNetXmlSerializer of RestSharp to make Microsoft's XmlSerializer do the actual serialization. Then you can use XML serialization attributes to specify that the List<AnimalsEnum> of Bar should be serialized without an outer container element by applying [XmlElement]:
public class Bar
{
public string Name { get; set; }
[System.Xml.Serialization.XmlElement]
public List<AnimalsEnum> Foo { get; set; }
public enum AnimalsEnum { Tiger, Elephant, Monkey }
}
Then, when making the request, do:
var req = new RestSharp.RestRequest(RestSharp.Method.POST);
// Use XmlSerializer to serialize Bar
req.XmlSerializer = new RestSharp.Serializers.DotNetXmlSerializer();
req.RequestFormat = RestSharp.DataFormat.Xml;
req.AddQueryParameter("REST-PAYLOAD", "");
req.AddXmlBody(myBar);
Note that Bar must be public because XmlSerializer can only serialize public types.
I have a asp.net webapi v2 running katana and using XmlSerializer with a very simple custom Serialization implementation:
config.Formatters.XmlFormatter.UseXmlSerializer = true;
config.Formatters.XmlFormatter.SetSerializer<SalesOrder>(new XmlSerializer<SalesOrder>());
XmlSerializer is just a simple implementation of the to System.Runtime.Serialization.XmlObjectSerializer abstract class
When calling a Post like this:
public HttpResponseMessage Post([FromBody]SalesOrder value)
{
return value == null || value.SignZoneCustomerNumber.IsNullOrEmpty()
? Request.CreateResponse(HttpStatusCode.BadRequest)
: Request.CreateResponse(HttpStatusCode.Accepted, _service.CreateOrder(value, Request.GetCorrelationId()));
}
The value is null when the xml contains a declaration like this
<?xml version="1.0" encoding="ISO-8859-15"?>
Strip out the declaration and send the post it desearilizes fine.
Any idea why the declaration causes deseriazation to fail?
The Model is defined like this:
[XmlRoot("Order")]
[Serializable]
public class SalesOrder : BaseOrder<SalesOrderLineItem>
{
public string PoNumber { get; set; }
public DateTime? PoDate { get; set; }
...more properties...
}
Thanks
UPDATE
Turns out it was the encoding that caused the problem
this header works:
<?xml version="1.0" encoding="UTF-8"?>
other values like UTF-16 and ISO-8859-1 fail
any idea why?
I want to deserialize XML to object in C#, object has one string property and list of other objects.
There are classes which describe XML object, my code doesn't work (it is below, XML is at end of my post). My Deserialize code doesn't return any object.
I think I do something wrong with attributes, could you check it and give me some advice to fix it.
Thanks for your help.
[XmlRoot("shepherd")]
public class Shepherd
{
[XmlElement("name")]
public string Name { get; set; }
[XmlArray(ElementName = "sheeps", IsNullable = true)]
[XmlArrayItem(ElementName = "sheep")]
public List<Sheep> Sheeps { get; set; }
}
public class Sheep
{
[XmlElement("colour")]
public string colour { get; set; }
}
There is C# code to deserialize XML to objects
var rootNode = new XmlRootAttribute();
rootNode.ElementName = "createShepherdRequest";
rootNode.Namespace = "http://www.sheeps.pl/webapi/1_0";
rootNode.IsNullable = true;
Type deserializeType = typeof(Shepherd[]);
var serializer = new XmlSerializer(deserializeType, rootNode);
using (Stream xmlStream = new MemoryStream())
{
doc.Save(xmlStream);
var result = serializer.Deserialize(xmlStream);
return result as Shepherd[];
}
There is XML example which I want to deserialize
<?xml version="1.0" encoding="utf-8"?>
<createShepherdRequest xmlns="http://www.sheeps.pl/webapi/1_0">
<shepherd>
<name>name1</name>
<sheeps>
<sheep>
<colour>colour1</colour>
</sheep>
<sheep>
<colour>colour2</colour>
</sheep>
<sheep>
<colour>colour3</colour>
</sheep>
</sheeps>
</shepherd>
</createShepherdRequest>
XmlRootAttribute does not change the name of the tag when used as an item. The serializer expects <Shepherd>, but finds <shepherd> instead. (XmlAttributeOverrides does not seem to work on arrays either.) One way to to fix it, is by changing the case of the class-name itself:
public class shepherd
{
// ...
}
An easier alternative to juggling with attributes, is to create a proper wrapper class:
[XmlRoot("createShepherdRequest", Namespace = "http://www.sheeps.pl/webapi/1_0")]
public class CreateShepherdRequest
{
[XmlElement("shepherd")]
public Shepherd Shepherd { get; set; }
}
So I'm writing my first mvc page, and I'm trying to write a series of routes to allow a reporting system to create simple reports. The xml is small, here is an example:
<xml><root><item><value>23</value></item></root>
I tried this:
using (StringWriter xmlStringWriter = new StringWriter())
{
using (XmlWriter xmlWriter = XmlWriter.Create(xmlStringWriter))
{
XmlWriter.WriteStartElement("root")
...
}
return xmlStringWriter.ToString();
}
but this obviously returns a string and is not interpreted as xml by the browser. I also know* that if you return an object that is serializable then the browser knows to interpret that as xml or json. So I tried defining a set of objects to hold each other in the way the xml is nested:
[Serializable]
public class XmlReportRoot
{
[System.Xml.Serialization.XmlAttribute("root")]
public List<XmlReportItem> item { get; set; }
}
[Serializable]
public class XmlReportItem
{
[System.Xml.Serialization.XmlAttribute("item")]
public XmlReportValue value { get; set; }
}
[Serializable]
public class XmlReportValue
{
[System.Xml.Serialization.XmlAttribute("value")]
public string count { get; set; }
}
and:
XmlReportRoot xmlRoot = new XmlReportRoot();
XmlReportItem xmlItem = new XmlReportItem();
List<XmlReportItem> itemList = new List<XmlReportItem>();
itemList.Add(xmlItem);
XmlReportValue xmlValue = new XmlReportValue();
xmlValue.count = newCustomers.ToString();
xmlItem.value = xmlValue;
xmlRoot.item = itemList;
XmlSerializer xmlSer = new XmlSerializer(typeof(XmlReportRoot));
xmlSer.Serialize(xmlRoot); //this line doesn't work
but this just feels wrong, and I couldn't quite get the serialization to work without worrying about a file stream, which I would rather do.
So I guess I was trying to find a way to do something like XmlWriter but be able to serialize that without an object type and return that, instead of having to worry about custom serializable objects types.
Use XmlWriter.Create(Response.OutputStream) and Response.ContentType = "application/xml"
I am currently working on a project that requires me to output XML from its endpoints along with JSON. I have the following model:
[DataContract(Namespace="http://www.yale.edu/tp/cas")]
[XmlType("serviceResponse")]
[XmlRoot(Namespace="http://www.yale.edu/tp/cas")]
public class ServiceResponse
{
[XmlElement("authenticationSuccess")]
public AuthenticationSuccess Success { get; set; }
[XmlElement("authenticationFailure")]
public AuthenticationFailure Failure { get; set; }
}
The output is as follows when success is not null:
<serviceResponse>
<authenticationSuccess />
</serviceResponse>
Now, I can see that obviously, I don't have a prefix assigned to the namespace I told the elements to be a part of. My issue is that I cannot find a place to add the namespace prefixes in MVC4 using the media formatter. I have the following in my global.asax:
GlobalConfiguration.Configuration.Formatters.XmlFormatter.UseXmlSerializer = true;
GlobalConfiguration.Configuration.Formatters.XmlFormatter.RemoveSerializer(typeof(Models.ServiceResponse));
GlobalConfiguration.Configuration.Formatters.XmlFormatter.SetSerializer(typeof(Models.ServiceResponse), new Infrastructure.NamespaceXmlSerializer(typeof(Models.ServiceResponse)));
I made a custom serializer based on XmlSerializer in an attempt to intercept the writing request and tack the namespace list on there. The issue with this method is that right now I have breakpoints inside every overrideable method and none of them are tripped when serializing which leads me to believe that my serializer isn't being used.
Is there some built in way to accomplish what I want to do or am I stuck re-implementing the XmlMediaTypeFormatter to pass in namespaces when it serializes objects?
As a followup answer: The easiest solution for me was to write my own XmlMediaTypeFormatter. As it turns out, its not nearly as intimidating as I thought.
public class NamespacedXmlMediaTypeFormatter : XmlMediaTypeFormatter
{
const string xmlType = "application/xml";
const string xmlType2 = "text/xml";
public XmlSerializerNamespaces Namespaces { get; private set; }
Dictionary<Type, XmlSerializer> Serializers { get; set; }
public NamespacedXmlMediaTypeFormatter()
: base()
{
this.Namespaces = new XmlSerializerNamespaces();
this.Serializers = new Dictionary<Type, XmlSerializer>();
}
public override Task WriteToStreamAsync(Type type, object value, System.IO.Stream writeStream, System.Net.Http.HttpContent content, System.Net.TransportContext transportContext)
{
lock (this.Serializers)
{
if (!Serializers.ContainsKey(type))
{
var serializer = new XmlSerializer(type);
//we add a new serializer for this type
this.Serializers.Add(type, serializer);
}
}
return Task.Factory.StartNew(() =>
{
XmlSerializer serializer;
lock (this.Serializers)
{
serializer = Serializers[type];
}
var xmlWriter = new XmlTextWriter(writeStream, Encoding.UTF8);
xmlWriter.Namespaces = true;
serializer.Serialize(xmlWriter, value, this.Namespaces);
});
}
}
Here is the formatter as a gist: https://gist.github.com/kcuzner/eef239003d4f99dfacea
The formatter works by exposing the XmlSerializerNamespaces that the XmlSerializer is going to use. This way I can add arbitrary namespaces as needed.
My top model looks as follows:
[XmlRoot("serviceResponse", Namespace="http://www.yale.edu/tp/cas")]
public class ServiceResponse
{
[XmlElement("authenticationSuccess")]
public CASAuthenticationSuccess Success { get; set; }
[XmlElement("authenticationFailure")]
public CASAuthenticationFailure Failure { get; set; }
}
In my Global.asax I added the following to place my formatter on the top of the list:
var xmlFormatter = new Infrastructure.NamespacedXmlMediaTypeFormatter();
xmlFormatter.Namespaces.Add("cas", "http://www.yale.edu/tp/cas");
GlobalConfiguration.Configuration.Formatters.Insert(0, xmlFormatter);
After adding the formatter and making sure my attributes were set up properly, my XML was properly namespaced.
In my case, I needed to add the cas namespace linking to http://www.yale.edu/tp/cas. For others using this, just change/replicate the Add call to your heart's content adding namespaces.