I've recently started using RestSharp to consume a REST service which uses XML.
It makes deserializing objects from XML to a collection of custom objects trivial. But my question is what is the best way to reserialize when posting back to the service?
Should I use LINQ-to-XML to reserialize? I tried using the Serializeable attribute and a SerializeToXml utility function, but when I do so it seems to break the deserializing performed by RestSharp.
I have been able to use attributes to get all of what I need, although my situation is relatively simple. For example, to get it to deserialize nodes with dashes in them, and then to be able to serialize to the same node name I used this:
[XmlElement(ElementName = "short-name")]
[SerializeAs(Name = "short-name")]
public string shortName { get; set; }
So, in your example, serialization doesn't respect [XmlElement("elementName")]. Instead, you will need to use [SerializeAs(Name = "elementName")].
I found this by trolling through the test code in the RestSharp project.
After looking at the source code for RestSharp I found that they actually have a built in wrapper for System.Xml.Serialization.XmlSerializer named DotNetXmlSerializer, it's just not used by default. To use it, just add the following line:
var request = new RestRequest();
request.RequestFormat = RequestFormat.Xml;
request.XmlSerializer = new DotNetXmlSerializer();
request.AddBody(someObject);
On a recent project I used XElement (from the System.Xml.Linq assembly) to manually build up my requests. I only had a handful of properties to deal with though. RestSharp solved the real problem which was deserializing the large XML graph responses from the server.
If your object model is dissimilar to XML schema you will have to create another object model, and map to that, just so it can be serialized automagically, using some library. In that situation you may be better off manually mapping to the schema.
RestSharp supports some basic XML serialization, which you can override if needed:
var request = new RestRequest();
request.RequestFormat = RequestFormat.Xml;
request.XmlSerializer = new SuperXmlSerializer(); // optional override, implements ISerializer
request.AddBody(person); // object serialized to XML using current XML serializer
Related
.NET Core 2.x used to generate "normal" XML in a format that was easily deserializeable using standard methods, but now, in v3.1.5 at least, using AddXmlDataContractSerializerFormatters in your startup configuration to enable an API to return XML returns responses that look like this...
<ArrayOfArrayOfKeyValueOfstringanyType xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<ArrayOfKeyValueOfstringanyType>
<KeyValueOfstringanyType>
<Key>districtId</Key>
<Value xmlns:d4p1="http://www.w3.org/2001/XMLSchema" i:type="d4p1:decimal">122</Value>
</KeyValueOfstringanyType>
<KeyValueOfstringanyType>
<Key>amount</Key>
<Value xmlns:d4p1="http://www.w3.org/2001/XMLSchema" i:type="d4p1:decimal">1888810.42</Value>
</KeyValueOfstringanyType>
</ArrayOfKeyValueOfstringanyType>
<ArrayOfKeyValueOfstringanyType>
<KeyValueOfstringanyType>
<Key>districtId</Key>
<Value xmlns:d4p1="http://www.w3.org/2001/XMLSchema" i:type="d4p1:decimal">205</Value>
</KeyValueOfstringanyType>
<KeyValueOfstringanyType>
<Key>amount</Key>
<Value xmlns:d4p1="http://www.w3.org/2001/XMLSchema" i:type="d4p1:decimal">1207736.00</Value>
</KeyValueOfstringanyType>
</ArrayOfKeyValueOfstringanyType>
</ArrayOfArrayOfKeyValueOfstringanyType>
When trying to deserialize this in client code using
var serializer = new XmlSerializer(typeof(List<MyDto>));
var reader = new StringReader(content);
var list = (List<MyDto>)serializer.Deserialize(reader);
it understandably throws the exception
InvalidOperationException: <ArrayOfArrayOfKeyValueOfstringanyType xmlns='http://schemas.microsoft.com/2003/10/Serialization/Arrays'> was not expected.
The DTO used in this case is simply
public class MyDto
{
public decimal DistrictId { get; set; }
public decimal Amount { get; set; }
}
so it is expecting XML similar to this, which would work fine
<ArrayOfMyDto>
<MyDto>
<DistrictId>122</DistrictId>
<Amount>1888810.42</Amount>
</MyDto>
... more items in list here
</ArrayOfMyDto>
Anyone know how to deserialize this XML format now returned in .NET Core 3.1.5?
UPDATE:
The real issue in my case is that the XML I need to deserialize looks like it does because is a serialized list of ExpandoObjects, not a list of regular DTOs, even though a regular DTO was the source for data in the expando objects.
In the case of a list of regular DTOs, I would have just used AddXmlSeralizerFormatters and it would deserialize lists of DTOs wrapped in "ArrayOf" just fine.
I performed a bunch of tests using AddXmlSerializerFormatters and AddXmlDataContractSerializerFormatters together with lists of regular DTOs and lists of ExpandoObjects to determine the behavior and what could be done without writing custom classes to deserialize this XML repsonse:
From a client perspective:
AddXmlSerializerFormatters CAN handle regular lists of regular objects (DTO) for either serialization or deserialization
AddXmlSerializerFormatters CANNOT handle lists of dynamic objects (ExpandoObject) for either serialization or deserialization (406 Not Acceptable)
AddXmlDataContractSerializerFormatters CAN handle serialization but NOT deserialization of lists of regular objects (DTO) (InvalidOperationException)
AddXmlDataContractSerializerFormatters CAN handle serialization but NOT deserialization of lists of dynamic objects (ExpandoObject) (InvalidOperationException)
Using both Formatters together, with AddXmlSerializerFormatters first in the pipeline, DOES allow serialization and deserialization to be passed through to AddXmlDataContractSerializerFormatters because the former returns 406 Not Acceptable, and the latter’s behaviors is observed
Using both Formatters together, with AddXmlDataContractSerializerFormatters first in the pipeline, DOES NOT allow this pass through because InvalidOperationException is returned and halts the processing
From a server perspective:
AddXmlSerializerFormatters CANNOT handle POSTs of even regular objects (DTO) (400 Bad Request) and DOES NOT pass through to AddXmlDataContractSerializerFormatters which CAN handle it
So, in conclusion, the best behavior you can expect without having to write custom DTOs for “ArrayOf” wrapped XML, is to use AddXmlSerializerFormatters followed by AddXmlDataContractSerializerFormatters in your client app, and use ONLY AddXmlDataContractSerializerFormatters in your server app
Or better yet, ditch XML completely on your server and use JSON :P
private void deserializxml()
{
string myxmlstr = #"<ArrayOfMyDto>
<MyDto>
<DistrictId>122</DistrictId>
<Amount>1888810.42</Amount>
</MyDto>
</ArrayOfMyDto>";
XmlSerializer serializer = new XmlSerializer(typeof(List<MyDto>), new XmlRootAttribute("ArrayOfMyDto"));
StringReader stringReader = new StringReader(myxmlstr);
List<MyDto> addList = (List<MyDto>)serializer.Deserialize(stringReader);
}
I am creating a web api which need to create large XML responses according to various requests. I tried googling but all the answers are about XMLSerializer.
And I tried making XDocument object and returning it using toString() method.
But it's not working properly too. It gives me output like this
<string xmlns="http://schemas.microsoft.com/2003/10/Serialization/"><root> <someNode>someValue</someNode> </root></string>
Isn't there an easy way to generate XML dynamically and return it.
Instead of generating XML dynamically, Why don't to use collection of class. Make a generic class and assign the data in collection. Send the collection using media header as xml.
request.Headers.Accept.Clear();
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
I've successfully set up some classes that use TPH EF inheritance, MyBaseClass, MySubClass1, MySubClass2 etc.
When querying using Linq context.MyBaseClasses.Where(...), the objects returned all correctly use the subclass specified by the Discriminator field in the database. (So I might end up with a list containing a mix of objects of MySubClass1, or MySubClass2.)
However, when I pass these objects to a WPF application, via a JSON Web Api call, the objects received are all of MyBaseClass, rather than the correct sub class they started off at.
The object property they are returned via is of type public virtual List<MyBaseClass> MyThings, so I guess it makes sense that they all end up as this type, but I want to retain the correct sub class type for each object.
How do I achieve this? Do I need to force the EF Discriminator field to be sent along with all the other data somehow?
Edit
1.
At the client end, I'm now attempting to deserialize the JSON (including $type data) like so (with no luck, the items are still converted back to their base class)
HttpResponseMessage response = GetClient().GetAsync(url).Result;
if (response.IsSuccessStatusCode)
{
string jsonMessage;
using (Stream responseStream = response.Content.ReadAsStreamAsync().Result)
{
jsonMessage = new StreamReader(responseStream).ReadToEnd();
}
List<My.Domain.Models.MyBaseClass> thingsToReturn;
//new method
thingsToReturn = JsonConvert.DeserializeObject<List<My.Domain.Models.MyBaseClass>>(jsonMessage);
//previous method
//thingsToReturn = response.Content.ReadAsAsync<List<My.Domain.Models.MyBaseClass>>().Result;
return thingsToReturn;
}
2.
Following Anish's advice re SerializerSettings.TypeNameHandling, I've now got $type information appearing in my JSON, however this is of type System.Data.Entity.DynamicProxies.SubClass1_5E07A4CE2F037430DC7BFA00593.... is this OK for the client end to deserialize back into SubClass1 successfully?
This is a serialization concern.
You can customize Json.Net's serializer settings to include type information with your json objects.
Add this to your HttpConfiguration:
config.Formatters.JsonFormatter.SerializerSettings.TypeNameHandling = TypeNameHandling.Auto;
Where config is the HttpConfiguration instance that you use to configure and initialize Asp.Net WebApi.
This will tell Json.Net to add some type information to each json object that has type ambiguity. Such an object would look like this:
{
"$type":"MyProjectContainingMyTypes.MySubClass1, MyProjectContainingMyTypes",
"Name": "Tyrion Lannister",
"DisplayName": "The Imp",
"Traits": ["funny", "awesome", "clever"]
}
Json.Net will know how to deal with this when you deserialize this on the WPF side.
This should work, in your WPF app:
var things = JsonConvert.DeserializeObject<List<MyBaseClass>>(jsonString);
Then you can cast the objects in the things list to their respective derived types.
Of course, your WPF application will need to have a reference to the project where you define MyBaseClass and MySubClass1.
Edit
Thanks Anish, that's almost sorted it. I can see the correct $type data in the JSON, I'm just being a dunce, and I'm not sure how to get the WebApi response as a jsonString? At the minute I'm doing response.Content.ReadAsAsync>().Result; to auto deserialize the data.
In response to your comment, you can read the content as a string like this:
var jsonString = response.Content.ReadAsStringAsync().Result;
Edit 2
Anish, thanks so much for your input. As you can see, I've managed to get the JSON as a string now, but even using JsonConvert.DeserializeObject I'm still getting the same issue. Do you think it is (as I mention in Edit 2) to do with the $type being returned from the service incorrectly (as an EF proxy type)?
In response to your comment, yes you will not be able to deserialize the EF proxy type into the type you want. This issue needs to be resolved on the WebApi side.
The proxy classes are created to allow you to lazy load entities. Proxies are generally used represent referenced/nested entities. This allows the fetching of referenced entities from the database to be deferred until required, if required at all.
Here is a link to some documentation around lazy and eager loading entities with EF.
Solution
You want to hydrate the list of objects in your WebApi controller action and return it, this will tell EF to load the entities from the database and new up instances of your classes.
You have a few options here:
Option 1
Call ToList() on the query to hydrate the collection:
var result = (from t in dbContext.Things select t).ToList();
or
var result = dbContext.Things.ToList();
Naturally, you don't want to return an unbounded result set so add a range:
var result = (from t in dbContext.Things select t).Skip(0).Take(10).ToList();
or
var result = dbContext.Things.Skip(0).Take(10).ToList();
Bear in mind that with method you will have to explicitly hydrate nested objects like this:
var result = dbContext
.Things
.Include(t => t.SomePropertyThatRepresentsSomeNestedObject)
.Skip(0)
.Take(10)
.ToList();
Option 2
Turn off lazy loading for your DbContext.
Personally, I'd go with Option 1, I think it is better to know your entities and have control over when and what you hydrate.
Many, many thanks to Anish, who's answer set me off on the right track, along with this excellent article.
The steps I had to take to get the types of the inherited objects to pass through the web api service are as follows:
Server Side
The key things were to set the JsonFormatter Serializer TypeNameHandling setting to TypeNameHandling.Auto. This can be done in WebApiConfig.Register(), but it will then obviously add a $type property to all JSON objects returned by your web service calls, or, you can simply decorate the property of the object you need the $type for.
WebApiConfig Method
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.TypeNameHandling = Newtonsoft.Json.TypeNameHandling.Auto;
Property Decoration Method
[Newtonsoft.Json.JsonProperty(ItemTypeNameHandling = Newtonsoft.Json.TypeNameHandling.Auto)]
public virtual List<MyBaseClass> Things { get; set; }
To get the correct value in the $type property in the JSON, and not the EF proxy class name, I turned off the ProxyCreationEnabled property before performing the Linq query that returned the objects based on MyBaseClass.
dbContext.Configuration.ProxyCreationEnabled = false;
List<MyBaseClass> things = dbContext.MyBaseClasses.Include("This").Include("That").ToList();
Client Side
I had to add a JsonMediaTypeFormatter with SerializerSettings = { TypeNameHandling = TypeNameHandling.Auto } to the ReadAsAsync() call and then the (correctly typed) objects mapped to their sub class happily.
HttpResponseMessage response = GetClient().GetAsync(url).Result;
if (response.IsSuccessStatusCode)
{
//this formatter responds to the $type parameter passed in the JSON to allow us to correctly map object types
//https://kirmir.wordpress.com/2014/05/16/polymorphic-serialization-using-newton-json-net-in-httpcontent/
var formatter = new JsonMediaTypeFormatter
{
SerializerSettings = { TypeNameHandling = TypeNameHandling.Auto }
};
List<MyBaseClass> thingsToReturn;
thingsToReturn = response.Content.ReadAsAsync<List<MyBaseClass>>(new List<MediaTypeFormatter> { formatter }).Result;
return productTestsToReturn;
}
I have create a class called RESTObjectBase. This class is inherited by any number of other classes that include information like persons, address, phone numbers.
I create an List<RestObjectBase> containing one or more objects of this type and pass it back to the front end via a REST call.
The JSON that is return is as follows:
{
"Items":[
{
"__type":"AnyObjectInheritedFromBaseClass",
"ObjectType":3,
"First_Name":"",
"Last_Name":"",
}
],
"Status":0
}
I am trying to deserialize this at the other end. I've had multiple errors including 'type cannot be null' but that's not really my issue.
Is there any way to deserialize an object of this complexity to the original class/list. Why I'm struggling is it's an array of objects that ultimately inherit RESTObjectBase and it doesn't look like the deserializing is smart enough to do this.
The front end deserializing this could be any device or platform so I cannot simply use something like JSON.NET as it's not supported everywhere.
I would even be happy to deconstruct the JSON and serialize each item in the array separately but I cannot figure out how to do this either. I could use the type to work out how to deserialize it.
Any help would be appreciated.
It is all about using the correct serializer. I was serializing with DataContractJsonSerializer so did the same with the deserialize and it worked as expected.
RESTResponse rr2 = null;
using (MemoryStream ms = new MemoryStream())
{
var writer = new StreamWriter(ms);
writer.Write(textBox1.Text);
writer.Flush();
ms.Position = 0;
DataContractJsonSerializer s = new DataContractJsonSerializer(typeof(RESTResponse));
rr2 = (RESTResponse)s.ReadObject(ms);
}
Although I plan I doing this on many different platforms on mobile devices, I am planning on using Xamarin so I should be able to do the same code.
This also means I could us JSON.NET.
In my .net code I am consuming a third-party asmx service that provides me data in Xml format. So basically, I am recieving a structure in a form of XmlNode:
<PostcodeEntry>
<Postcode>13542</Postcode>
<Postcodename>Odessa</Postcodename>
</PostcodeEntry>
Currently, to map it to my POCO object I have to manually iterate through a corresponding ChildNode's and retrieve their InnerText value to get the actual data:
var PostCodeNode = entryNode.SelectSingleNode("Postcode");
if (PostCodeNode != null)
{
result.PostCode = PostCodeNode.InnerText;
}
In case I need to map a large info structure, the code grows to a messy code-scroll.
Is there a way I can improve this so I don't have to write the parsing manually? What is the best practice for this?
I believe that you have different options depending on how you get your data and how you like to design your code etc. From your brief description I can think of at least these two:
Create an XML Serializer - for example by marking up your class with Xml Attributes and de-serialize the XML directly as your desired object via the serializer. The disadvantage of this approach is that you will create a strong coupling between your serializer and your business object. Please take a look at something like this: http://www.switchonthecode.com/tutorials/csharp-tutorial-xml-serialization.
Create a proxy object and map your proxy object to your business object. You can create the proxy object either by using a WSDL exposed by the asmx service, or by using the XSD.exe tool or similar (you may need to first generate an XSD based on the XML if the XML is not already described by an XSD). Then you can map the properties of your proxy object to the properties of your business object. This will provide you a more clean separation between the objects, but at the same time it requires more work.
Br. Morten
You can create SoapClient object for WebService, then you can return the Response as List<>. You need to change the Ouput response to List<>.
example Consilder this the webservice to consume, http://xxx.xx.xxx.xxx/CosmosService/Cm_Service.asmx
then add Service Reference in your application, Click On Advanced Button, change the Collection Type System.Collections.GenericList.
then you can Consume WebService Methods as List<> directly like this
CosmosRef.CM_ServiceSoapClient client = new CosmosRef.CM_ServiceSoapClient();
List<CosmosRef.Product> listProduct = client.GetAllProducts("Computers", 1);
dataGrid1.DataContext = listProduct;