Efficiency of deserialization vs. XmlReader - serialization

I'm working with a complicated xml schema, for which I have created a class structure using xsd.exe (with some effort). I can now reliably deserialize the xml into the generated class structure. For example, consider the following xml from the web service:
<ODM FileType="Snapshot" CreationDateTime="2009-10-09T19:58:46.5967434Z" ODMVersion="1.3.0" SourceSystem="XXX" SourceSystemVersion="999">
<Study OID="2">
<GlobalVariables>
<StudyName>Test1</StudyName>
<StudyDescription/>
<ProtocolName>Test0001</ProtocolName>
</GlobalVariables>
<MetaDataVersion OID="1" Name="Base Version" Description=""/>
<MetaDataVersion OID="2" Name="Test0001" Description=""/>
<MetaDataVersion OID="3" Name="Test0002" Description=""/>
</Study>
</ODM>
I can deserialize the xml as follows:
public ODMcomplexTypeDefinitionStudy GetStudy(string studyId)
{
ODMcomplexTypeDefinitionStudy study = null;
ODM odm = Deserialize<ODM>(Service.GetStudy(studyId));
if (odm.Study.Length > 0)
study = odm.Study[0];
return study;
}
Service.GetStudy() returns an HTTPResponse stream from the web service. And Deserialize() is a helper method that deserializes the stream into the object type T.
My question is this: is it more efficient to let the deserialization process create the entire class structure and deserialize the xml, or is it more efficient to grab only the xml of interest and deserialize that xml. For example, I could replace the above code with:
public ODMcomplexTypeDefinitionStudy GetStudy(string studyId)
{
ODMcomplexTypeDefinitionStudy study = null;
using (XmlReader reader = XmlReader.Create(Service.GetStudy(studyId)))
{
XDocument xdoc = XDocument.Load(reader);
XNamespace odmns = xdoc.Root.Name.Namespace;
XElement elStudy = xdoc.Root.Element(odmns + "Study");
study = Deserialize<ODMcomplexTypeDefinitionStudy>(elStudy.ToString());
}
return study;
}
I suspect that the first approach is preferred -- there is a lot of dom manipulation going on in the second example, and the deserialization process must have optimizations; however, what happens when the xml grows dramatically? Let's say the source returns 1 MB of xml and I'm really only interested in a very small component of that xml. Should I let the deserialzation process fill up the containing ODM class with all it's arrays and properties of child nodes? Or just go get the child node as in the second example!!??
Not sure this helps, but here's a summary image of the dilemma:

Brett,
Later versions of .net will build custom serializer assemblies. Click on project properties -> build and look for "Generate serialization assemblies" and change to On. The XML deserializer will use these assemblies which are customized to the classes in your project. They are much faster and less resource intensive since reflection is not involved.
I would go this route so that if you class changes you will not have to worry about serialization issues. Performance should not be an issue.

I recommend that you not preoptimize. If you have your code working, then use it as it is. Go on to work on some code that is not finished, or which does not work.
Later, if you find you have a performance problem in that area, you can explore performance.

Related

jackson-dataformat-csv: cannot serialize LocalDate

When I try to serialize object containing Local date, I get following error:
csv generator does not support object values for properties
I have JSR-310 module enabled, with WRITE_DATES_AS_TIMESTAMPS and I can convert the same object to JSON without problem.
For now I resorted to mapping the object to another, string only object, but it's decadent and wasteful.
Is there a way for Jackson csv mapper to acknowledge localDates? Should I somehow enable JSR-310 specifically for csv mapper?
I had the same problem because of configuring mapper after schema. Make sure you are using the latest verson of jackson and its modules. This code works for me:
final CsvMapper mapper = new CsvMapper();
mapper.findAndRegisterModules();
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); //Optional
final CsvSchema schema = mapper.schemaFor(PojoWithLocalDate.class);
// Use this mapper and schema as you need to: get readers, writers etc.
No additional annotations needed in Pojo class.

IQueryable serialization (web api)

Why isn't this working?:
var surveys = db.Surveys.Where(s => s.Author.UserId == user.UserId);
return from survey in surveys
select new
{
surveyId = survey.SurveyId,
title = survey.Title
};
And this, with a minor change, is?:
var surveys = db.Surveys.Where(s => s.Author == user);
return from survey in surveys
select new
{
surveyId = survey.SurveyId,
title = survey.Title
};
It throws a serialization error
The 'ObjectContent`1' type failed to serialize the response body for content type
'application/xml; charset=utf-8'. (...)
I'm fine with solving it that way, but I have the same error here (below), and can't solve it the same way:
var surveys = db.Surveys.Where(s => s.AnswerableBy(user));
I ran into this issue recently, in my case the problem was that I had circular references created by the Entity Framework - Model First (Company -> Employee -> Company).
I resolved it by simply creating a view model object that had only the properties I needed.
As per the section 'Handling Circular Object References' here on the Microsoft ASP.NET Web API website, add the following lines of code to the Register method of your WebAPIConfig.cs file (should be in the App_Start folder of your project).
var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
config.Formatters.Remove(config.Formatters.XmlFormatter);
There are separate instructions on that page for dealing with XML parsing.
The exception you are seeing is a general exception, which can be caused by any number of factors. Check the InnerException property of the serialization exception to find out what exactly caused the serialization to fail.
You are using an anonymous object. Make it strongly typed and the serialization will work.
At the end this has to do with Linq to Entities vs Linq to Objects.
Some LINQ queries can not be translated into SQL (like using a method: someCollection.Where(p => p.SomeMethod() == ...)). So they must be translated to memory first (someCollection.ToList().Where...)
I was having the same problem. In my case the error was caused by the relation between tables created in my Dbml file. Once I removed the relation from the DBML file, it worked. I think Database relations can't be serialized.

How can I POST (as XML) an object to my ApiController using RestSharp?

I have an ASP.NET MVC4 website implementing a REST API, which I'm consuming from a client application. My ApiController methods take and return complex objects, as XML.
I recently discovered RestSharp, and have begun moving my client project over to that. However, I'm having real problems with it. It seems to almost work - it's so close that I can almost taste success - but I just can't get it to work 100%.
The objects I'm passing across the wire look something like this:
// The object I'm passing across the wire
public class Example
{
bool IsActive { get; set; }
string Name { get; set; }
}
My ApiController method looks like this:
// My ApiController method
public HttpResponseMessage PostExample(Example example)
{
if (ModelState.IsValid)
{
db.Examples.Add(example);
db.SaveChanges();
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created, example);
return response;
}
else
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
}
The problem occurs when I try to POST an object to my website, like this:
var example = new Example () { IsActive = true, Name = "foo" };
var request = new RestSharp.RestRequest("/api/example", RestSharp.Method.POST);
request.AddBody(example, XmlNamespace);
var client = new RestClient();
client.BaseUrl = "foo.com";
var response = client.Execute<Example>(request);
The code above does hit the PostExample method in my ApiController, and it has an Example object as the parameter. However the values of the properties of the Example object are not the same as I passed to the Execute method! In one case, the IsActive member was false instead of true, although I also saw a case where the Name member was null where it should have had a value.
I did some investigation using Fiddler, and it seems that the correct values are being created in the XML that RestSharp generates. However, the XML is not quite in the same format that the web server emits when doing a GET. The differences are subtle, but seem to make the difference between it working and not working. The framework at the web server end seems to be sensitive to these formatting differences, and is mis-interpreting the XML as a result.
Here's the XML I get from RestSharp:
<Example xmlns="http://schemas.datacontract.org/2004/07/ExampleNamespace">
<Name>foo</Name>
<IsActive>true</IsActive>
</Example>
This is what I get when doing a GET on the webserver (or when serializing using the DataContractSerializer, which is what I was previously doing):
<Example xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/ExampleNamespace">
<IsActive>true</IsActive>
<Name>foo</Name>
</TagDto>
The RestSharp version has the following differences from the DataContractSerializer's version:
Fields are in a different order
RestSharp doesn't include the extra namespace XMLSchema-instance namespace
DataContractSerializer doesn't include any spaces or line-breaks (I added those above for readability)
I'm surprised that any of those make much of a difference, but clearly they do. Note also that until I added an explicit namespace in the AddBody call, this was missing in the generated XML (obviously), and the Example object passed into my ApiController was null.
Anyway, I noticed that RestSharp allows you to override the serializer, and provides a way to use the .NET XML serializer. I tried using that (to no avail).
This is what I added before the call to AddBody:
request.XmlSerializer = new RestSharp.Serializers.DotNetXmlSerializer(XmlNamespace);
..and this is what I get out:
<?xml version="1.0" encoding="utf-8"?>
<Example>
<Name>foo</Name>
<IsActive>true</IsActive>
</Example>
This is clearly no good, not least because it starts with an XML declaration, which I imagine would cause problems. There's no way to turn that off, because the RestSharp derived class provides no way to do so. Also, there's no namespace - and I can't get one to appear in the output no matter how I try to set the namespace in RestSharp (in the constructor for the DotNetXmlSerializer, by setting the Namespace member, or by passing in a namespace to AddBody). To my eyes, this class is nothing more than a trap.
It looks like my only option is to create my own serializer class and use the DataContractSerializer internally. Is that right, or am I missing something?
(BTW, I can set the RequestFormat of the request to JSON and it just works - but I'd still like to know how to get this working with XML).
I've had some issues with the AddBody calls not properly serializing JSON values, so there might be some similarity to your problem. Instead of AddBody, you could try:
request.AddParameter("text/xml", xmlAsString, ParameterType.RequestBody);
If that works, you could look to see about changing the second parameter to be the xml object and see if the serializer does what you want.
The other option could be the XmlMediaTypeFormatter.ReadFromStreamAsync isn't properly picking up a proper serializer; you could try overriding that function.
The issue above is because WebAPI is using the DataContractSerializer (as opposed to the XmlSerializer which is what you're after). To switch this around modify Global.asax as follows.
var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
xml.UseXmlSerializer = true;
However, I suggest you use the RESTSharp formatters for WebAPI (instead of using the .Net formatters). This is particularly useful if you're DTO's have circular references (the .net fx serializers don't handle this too gracefully).
In Global.asax, modify the formatters by putting in
GlobalConfiguration.Configuration.Formatters.XmlFormatter = //RestSharp XML serializer here
A quick overview of serialization in WebAPI is here and worth a browse

JAX-RS return a Map<String,String>

I want to retrieve a Map from a using JAX-RS (text/xml)
#GET
public Map<String,String> getMap(){
}
but I am getting the error below:
0000001e FlushResultHa E org.apache.wink.server.internal.handlers.FlushResultHandler handleResponse The system could not find a javax.ws.rs.ext.MessageBodyWriter or a DataSourceProvider class for the java.util.HashMap type and application/x-ms-application mediaType. Ensure that a javax.ws.rs.ext.MessageBodyWriter exists in the JAX-RS application for the type and media type specified.
[10:43:52:885 IST 07/02/12] 0000001e RequestProces I org.apache.wink.server.internal.RequestProcessor logException The following error occurred during the invocation of the handlers chain: WebApplicationException (500 - Internal Server Error) with message 'null' while processing GET request sent to http://localhost:9080/jaxrs_module/echo/upload/getSiteNames
The solution I choose is to wrap a Map and use it for the return param.
#XmlRootElement
public class JaxrsMapWrapper {
private Map<String,String> map;
public JaxrsMapWrapper(){
}
public void setMap(Map<String,String> map) {
this.map = map;
}
public Map<String,String> getMap() {
return map;
}
}
and the method signature will go like this
#GET
public JaxrsMapWrapper getMap()
Your problem is that the default serialization strategy (use JAXB) means that you can't serialize that map directly. There are two main ways to deal with this.
Write an XmlAdaptor
There are a number of questions on this on SO but the nicest explanation I've seen so far is on the CXF users mailing list from a few years ago. The one tricky bit (since you don't want an extra wrapper element) is that once you've got yourself a type adaptor, you've got to install it using a package-level annotation (on the right package, which might take some effort to figure out). Those are relatively exotic.
Write a custom MessageBodyWriter
It might well be easier to write your own code to do the serialization. To do this, you implement javax.ws.rs.ext.MessageBodyWriter and tag it with #Provider (assuming that you are using an engine that uses that to manage registration; not all do for complex reasons that don't matter too much here). This will let you produce exactly the document you want from any arbitrary type at a cost of more complexity when writing (but at least you won't be having complex JAXB problems). There are many ways to actually generate XML, with which ones to choose between depending on the data to be serialized
Note that if you were streaming the data out rather than assembling everything in memory, you'd have to implement this interface.
Using CXF 2.4.2, it supports returning Map from the api. I use jackson-jaxrs 1.9.6 for serialization.
#Path("participation")
#Consumes({"application/json"})
#Produces({"application/json"})
public interface SurveyParticipationApi {
#GET
#Path("appParameters")
Map<String,String> getAppParameters();
....
}
With CXF 2.7.x use
WebClient.postCollection(Object collection, Class<T> memberClass, Class<T> responseClass)
,like this in your rest client code.
(Map<String, Region>) client.postCollection(regionCodes, String.class,Map.class);
for other collections use WebClient.postAndGetCollection().

Consume WCF Data service in client application throws error

I am working on WCF Data service which imported stored procedure, as below.
[WebGet]
public List<GetMTSearchResultTest_Result> GettMTSearchResultTest()
{
MediaMarketResearch_PRODEntities ent = new MediaMarketResearch_PRODEntities();
return ent.GetMTSearchResultTest().ToList();
}
when i consuming this in my client application it says error as "The closed type MMRClient.MMRServiceReference.GetMTSearchResultTest_Result does not have a corresponding element settable property."
I am getting this error while bind to the grid view as below.
DataServiceContext context = new DataServiceContext(new Uri("http://localhost:4131/MMRDataService.svc/"));
IEnumerable<GetMTSearchResultTest_Result> empResult = context.Execute<GetMTSearchResultTest_Result>(new Uri("http://localhost:4131/MMRDataService.svc/GettMTSearchResultTest"));
GridView1.DataSource = empResult;
GridView1.DataBind();
Note: I imported this stored proc as complex type.
Please advice me on this.
Regards,
Jaydeep
I think this link may help you (see the selected answer).
Essentially, what the solution may be is to create a partial class for GetMTSearchResultTest_Result and decorate it with a DataServiceKey attribute, providing a non-nullable column that acts as a primary key (although I don't think it has to be unique).
So your partial class would look something like:
[DataServiceKey("YourKeyColumnName")]
public partial class GetMTSearchResultTest_Result {
}
If you're just doing reads, I don't think you'll need any implementation.
Hopefully this works. Let me know if there are issues/questions and I'll update accordingly.
You can always make a new service reference to a non data service. That is to a normal WCF service. You can simply have a [ContractOperation] returning a list of the troubled "complex types" and that's it.
This way you would have two services the original data service and a new normal WCF service. But this shouldn't be such an issue. You don't have to make the troubled "complex type" as a Entity.