how to change root element of api output - c#

I have created an api that gives the output in form of xml. It is used to give sitemap for the website. It just includes the following code
public class SitemapApiController : ApiController
{
public urlset GetSitemap()
{
// code for data access etc
return urlsetObj;
}
}
the urlset class is very simple
[XmlRoot("urlset")]
public class urlset: List<url>
{
}
the url class is as follows
public class url
{
public string loc { get; set; }
public DateTime lastmod { get; set; }
public double priority { get; set; }
}
Now my problem is that output is coming as
<arrayofurls>
<url>.....
while i want it to be
<urlset>
<url>....
I have read the similar posts for this, but to no relief. As stated in Modify the xml array element name in serialized ASP.NET WebAPI object post, if i use
[XmlArray("urlset")]
atribute over api controller, then it gives error that attribute "XmlArray" is not valid in this declaration type. Pls help me so that i can return valid sitemap from the api.
Edit
The output according to this is still a bit different. Now it shows
<urlset>
<url>
<url>
<loc>
<url>........
There is an extra needless url attribute as parent of all the url attributes.

UPDATED
It took a little bit of work, but I believe I found what you are looking for.
A WebApi project should serialize XML using the namespace System.Runtime.Serialization, not the namespace System.Xml.Serialization.
Using this new serialization method, it becomes extremely simple to output the results exactly as you want.
The api controller GET method:
public urlset Get()
{
return new urlset
{
new url { loc = "1" },
new url { loc = "2" },
new url { loc = "3" }
};
}
The urlset class:
[CollectionDataContract(Name ="urlset", Namespace ="")]
public class urlset:List<url>
{
}
The url class:
[DataContract(Namespace = "")]
public class url
{
[DataMember]
public string loc { get; set; }
[DataMember]
public DateTime lastmod { get; set; }
[DataMember]
public double priority { get; set; }
}
The output:
<urlset xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<url>
<lastmod>2016-10-19T08:07:28.6867397-07:00</lastmod>
<loc>1</loc>
<priority>0</priority>
</url>
<url>
<lastmod>2016-10-15T08:07:28.6867397-07:00</lastmod>
<loc>2</loc>
<priority>0</priority>
</url>
<url>
<lastmod>2016-09-20T08:07:28.6867397-07:00</lastmod>
<loc>3</loc>
<priority>0</priority>
</url>
</urlset>
I realize the example you give in your question does not have the xmlns attribute in the root node. However, it seems there is no "easy" way to remove that.
Here are the resources I used to arrive at this solution:
StackOverflow: How to serialize xml into desired format in asp.net web api
MSDN: Collection Types in Data Contracts

Related

xml Deserialize a list of objects containing a list of objects in c#

I'm trying to deserialize a list of objects that in turn contain lists of other object. What I've got is the following.
The actual deserializing is done by the framework in a ApiContoller httpPost request. The endpoint looks like this:
[HttpPost]
public async Task<HttpResponseMessage> MethodCall([FromBody] XmlEntries entries)
{
....
}
The XmlEntries class looks like this:
[XmlRoot("Root")]
public class XmlEntries
{
[XmlArrayItem("XmlEntry")]
public List<XmlEntry> XmlEntries{ get; set; }
public XmlEntries()
{
XmlEntries = new List<XmlEntry>();
}
public XmlEntries(IEnumerable<XmlEntry> entries)
{
XmlEntries= entries.ToList();
}
}
The XmlEntry class look like this:
public class XmlEntry
{
[XmlArrayItem("XmlSubEntry")]
public List<XmlSubEntry> XmlSubEntries{ get; set; }
}
and the XmlSubEntry looks like this.
public class XmlSubEntry
{
string AttributeOne{ get; set; }
int? AttributeTwo{ get; set; }
}
I've been using fiddler to send the following XML
<?xml version="1.0" encoding="utf-8"?>
<Root xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<XmlEntries>
<XmlEntry>
<XmlSubEntries>
<XmlSubEntry>
<AttributeOne>P</AttributeOne>
<AttributeTwo>8</AttributeTwo>
</XmlSubEntry>
<XmlSubEntry>
<AttributeOne>S</AttributeOne>
<AttributeTwo>26</AttributeTwo>
</XmlSubEntry>
</XmlSubEntries>
</XmlEntry>
</XmlEntries>
</Root>
My problem is that the attributes of XmlSubEntry never gets correctly serialized. When i debug the MethodCall in apiController entries will be a list containing 1 XmlEntry with XmlSubEntries being a list of 2 XmlSubEntry, but the attributes (AttributeOne and AttributeTwo) are always null.
I have tried annotating the classes In all ways I can thing of but I still won't get the attributes to serialize correctry.
Is there any XML-ninjas around that can help me figure out what I'm doing wrong?
The best tip I can give you is to do this in reverse - add some data and serialise it to XML, see what it looks like. That will usually point to where you're wrong.
In this case, however, you're very close. The only issue you have is that you can't serialise private properties, so make them public:
public class XmlSubEntry
{
public string AttributeOne { get; set; }
public int? AttributeTwo { get; set; }
}

MVC Web API ignores XmlSerializerFormat and uses DataContractSerliazer instead

I have a DTO and a Domain project in my solution and an MVC front end with web api to expose data.
I have the web api controller set up and the action is getting my DTO object back from the DataService. That's all great, however, I want xml to be returned and I want some of the values to be in xml attributes e.g.
<root xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<lookups>
<lookup category="General" field="Alert Type" value="Lack of Transparency" entityid="2273"/>
<lookup category="General" field="Alert Type" value="Unfair Terms " entityid="2274"/>
<lookup category="General" field="Alert Type" value="Operator Concerns" entityid="2275"/>
...
</lookups>
<paymentmethods />
<affiliates />
</root>
The Lookup class is as follows:
[Serializable]
[XmlSerializerFormat]
public class Lookup
{
[XmlAttribute("category")]
public String Category { get; set; }
[XmlAttribute("field")]
public String Field { get; set; }
[XmlAttribute("value")]
public String Value { get; set; }
[XmlAttribute("entityid")]
public String EntityId { get; set; }
public Lookup(String Category, String Field, String Value, int? EntityId = null)
{
this.Category = Category;
this.Field = Field;
this.Value = Value;
this.EntityId = (EntityId != null ? EntityId.ToString() : null);
}
public Lookup() { }
}
initially I had my DTO objects (e.g Lookup, PaymentMethod and Affiliates) with my viewmodels, but moved them into my DTO project.
I have set UseXmlSerializer = true in my global.asax
Before moving the objects from hy viewmodels folder to the DTO project, it was working and I was getting the desired XML. AFTER moving, it appears to be ignoring the XmlSerializerFormat and using DataContractSerliazer.
So using DataMember attributes, I can format the xml, but obviously I can't set some properties to be serialised as xml attributes
Any thoughts on why it seems to be ignoring the [XmlSerializerFormat] and [XmlAttribute("field")] attributes?
I've read a few SO posts like:
XmlSerializer ignores [XmlAttribute] in WebApi and
How can you control .NET DataContract serialization so it uses XML attributes instead of elements?
I found the solution to this here:
http://www.asp.net/web-api/overview/formats-and-model-binding/json-and-xml-serialization#xml_pertype
Setting the xml Serializer in the Global.asax to work on a specific type worked for me :
var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
xml.UseXmlSerializer = true;
xml.SetSerializer<FullResponseRoot>(new XmlSerializer(typeof(FullResponseRoot)));
where FullResponseRoot is the name of the object that I am serialising (the classes mentioned above are properties of FullResponseRoot)
FullResponseRoot forms the root node of the generated xml

XML Element Selection

I am collecting XML data from a weather website xml file here.
I have created the following code, which finds the first instance of 'temp_c' and returns the value. This is the temperature at the current moment.
using (XmlReader reader = XmlReader.Create(new StringReader(weather)))
{
while (reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
if (reader.Name.Equals("temp_c"))
{
reader.Read();
temp_c = reader.Value;
}
break;
}
}
}
return temp_c
This returns the value of the first instance in the XML file called "temp_c" to the string called "temp_c"
What I would now like to do is use the Element in the XML document called "period" and the element found with period called "fcttext". When "period = 0" it means "today", 1 = tomorrow, etc; and I am after the "fcttext_metric" data that is associated with that period value.
I've been struggling to work the examples and XML reading codes I've found into the current code that I have here. Can anyone please point me in the right direction?
Edit
Here is an example of the XML file:
<forecast>
<forecastday>
<period>0</period>
<fcttext_metric>Sunny</fcttext_metric>
<forecastday>
<period>1</period>
<fcttext_metric>Cloudy</fcttext_metric>
This ended up being more annoying that I originally expected, but you can create an object graph from the XML using a DataContractSerializer and a set of classes that match the XML you are reading.
First, you create your classes, with appropriate DataContract attributes.
[DataContract(Name = "response", Namespace = "")]
public class WeatherData
{
[DataMember(Name = "forecast")]
public Forecast Forecast { get; set; }
}
[DataContract(Name = "forecast", Namespace = "")]
public class Forecast
{
[DataMember(Name = "txt_forecast")]
public TxtForecast TxtForecast { get; set; }
}
[DataContract(Name = "txt_forecast", Namespace = "")]
public class TxtForecast
{
[DataMember(Name = "forecastdays")]
public ForecastDay[] ForecastDays { get; set; }
}
[DataContract(Name = "forecastday", Namespace = "")]
public class ForecastDay
{
[DataMember(Name = "period", Order = 1)]
public int Period { get; set; }
public string FctText { get; set; }
[DataMember(Name = "fcttext", EmitDefaultValue = false, Order = 5)]
private CDataWrapper FctTextWrapper
{
get { return FctText; }
set { FctText = value; }
}
}
Heres where it got complicated. The fcttext element is CDATA, which the DataContractSerializer doesn't support by default.
Using the wonderful answer available at Using CDATA with WCF REST starter kits, you create a CDataWrapper class. I won't repeat the code here (because that would be pointless), just follow the link.
You can see that I've already used the CDataWrapper class above in order to work with the fcttext element.
Once you've got the class structure setup you can use the following code to extract the information you were after. You're just navigating the object graph at this point, so you can use whatever C# you want (I've used a simple LINQ query to find Period 0 and print out the fcttext for it).
var s = new DataContractSerializer(typeof(WeatherData));
var reader = XmlReader.Create("http://api.wunderground.com/api/74e1025b64f874f6/forecast/conditions/q/zmw:00000.1.94787.xml");
var o = (WeatherData)s.ReadObject(reader);
var firstPeriod = o.Forecast.TxtForecast.ForecastDays.Where(a => a.Period == 0).Single();
Console.WriteLine(firstPeriod.FctText);
You can extend the classes as necessary to give you access to additional fields inside the XML. As long as the DataMember names match the XML fields it will all work.
Here's a short summary of some problems I ran into for anyone interested:
I had to set the Namespace attributes of all of the classes to the empty string, because the XML doesn't have any namespace info in it.
The Order attributes were important on the ForecastDay class. If they are omitted, the DataContractSerializer ends up not reading the fcttext field in at all (because it thinks fcttext should come first? not sure why to be honest).

How to specify a name to which object will be serialized by WebApi?

My web api is returning a set of objects which are differ from the Domain object. Forexample, I my domain has an Employee class but I don't want to expose all the members of the Employee class in my api so I created another class called EmployeeApiModel.
Now my WebApi is returning a List of EmployeeApiModel but I want to be able to specify the name to which it should serialize to. That is instead of <EmployeeApiModel> tag in the xml, I want to get <Employee> but without changing the fact that the underlying class which is being serialized is EmployeeApiModel.
How can I achieve this?
Technically, Web Api support both json and xml based on content negotiation mechanism, Json is the default format, if you want to receive xml, just put on header:
Accept: application/xml
To understand more content negotiation, access this
Since you want your api support both json and xml, you should use DataContract and DataMember Attribute for serialization for your model: EmployeeApiModel, something like:
[DataContract(Name = "Employee")]
public class EmployeeApiModel
{
[DataMember(Name = "Name2")]
public string Name { get; set; }
[DataMember]
public string Email { get; set; }
}
See more on this blog-post
You can control the output of your serialized XML by using various Attribute tags.
[XmlRoot("Employee")]
Public class EmployeeApiModel
{
[XmlElement("fname")]
public string FirstName { get; set; }
public string LastName { get; set; }
public int age { get; set; }
}
this will produce XML like:
<Employee>
<fname>John</fname>
<LastName >Smith</LastName >
<age>24</age>
</RootElementsName>
You can read more about the various XML modifiers here: http://msdn.microsoft.com/en-us/library/e123c76w.
If you want to use existing XML modifiers for JSON, check out this post: Serialize .Net object to json, controlled using xml attributes

How would I read into a 'nested' Json file with 'DataContractJsonSerializer' in C# .NET (win7 phone)?

I have an issue where if my json file looks like this
{ "Numbers": "45387", "Words": "space buckets"}
I can read it just fine, however if it looks like this:
{ "Main" :{ "Numbers": "45387", "Words": "space buckets"},
"Something" :{"Numbers": "12345", "Words": "Kransky"} }
I get no information back. I have no idea how to switch between Main and Something!
Loading a JSON with this 'nested' information using this code,
var ser = new DataContractJsonSerializer(typeof(myInfo));
var info = (myInfo)ser.ReadObject(e.Result);
// The class being using to hold my information
[DataContract]
public class myInfo
{
[DataMember(Name="Numbers")]
public int number
{ get; set; }
[DataMember(Name="Words")]
public string words
{ get; set; }
}
Causes the class to come back empty.
I've tried adding the group name to DataContract eg. [DataContract, Name="Main"] but this still causes the classes values to be empty.
I've also tried adding "main" to the serializer overloader eg. var ser = new DataContractJsonSerializer(typeof(myInfo), "Main");
This causes an error: Expecting element 'Main' from namespace ''.. Encountered 'Element' with name 'root', namespace ''.
I'd prefer to just use the supplied json reader. I have looked into json.NET but have found the documentation to be heavy on writing json and sparse with information about reading.
Surely I'm missing something simple here!
You could add a wrapper class:
[DataContract]
public class Wrapper
{
[DataMember]
public myInfo Main { get; set; }
[DataMember]
public myInfo Something { get; set; }
}
Now you could deserialize the JSON back to this wrapper class and use the two properties to access the values.

Categories