Can anyone explain me how ASP.NET handles the convertion from a class object to the JSON object in WebMethods?
For example, you have following WebMethod which returns a Person object:
[WebMethod]
public static Person GetPerson()
{
Person p = new Person()
{
Id = 1,
Name = "Test"
};
return p;
}
In my jQuery where i call the WebMethod I get a response which contains out of a json object.
How did ASP.NET do this automatcally? Does it uses the JavaScriptSerializer class?
Also you see a lot of examples of using JSON converters to convert your class object to a json object. Why is this? Is it because of the JavaScriptSerializer class it uses and its bad performance or... ?
How did ASP.NET do this automatically?
Basically there's some code sitting between the web and the WebMethod that takes the request, figures out what it's requesting, finds your WebMethod and obtains the result, then serializes it back to the client based on the acceptable formats in the request header.
Does it uses the JavaScriptSerializer class?
Probably. I couldn't find anything out there that stated it. But it doesn't use any third party library. Since that's one is built in, that's a good assumption.
Also you see a lot of examples of using JSON converters to convert
your class object to a json object. Why is this? Is it because of the
JavaScriptSerializer class it uses and its bad performance or... ?
WebMethod technique can be very finicky and sometimes will refuse to return JSON, despite the accept headers. One way around that is to do something like this:
[WebMethod]
public static void GetPerson()
{
Person p = new Person()
{
Id = 1,
Name = "Test"
};
HttpContext.Current.Response.ResponseType = "application/json";
HttpContext.Current.Response.Write(JsonConvert.SerializeObject(p));
HttpContext.Current.Response.End();
}
You lose content negotiation (unless you manually implement it by inspecting the request headers), but you get more control over how it's serialized.
Related
I'm using Auth0 for a new app I'm writing using ASP.NET Core 1.0. It's working great so far, but I have bumped into something that is stumping me for some reason.
When the user logs in, Auth0 will pass claims back to my app. Auth0 has the ability to add custom data to the user, it is stored in JSON format and comes over as a list of claims.
One of the claims will look something like this:
Type "app_metadata"
Value "\"IsPublisherFor\":[\"p56\",\"p124\",\"p258\"]"
ValueType "JSON"
My question is, how would I convert that claim value into something I can work with? For example, the ability to do something like IsPublisherFor.Contains("p56");
I tried to pass the value to NewtonSoft.Json.JsonConvert.DeserializeObject(value) but it throws an exception. JsonReaderException: Additional text encountered after finished reading JSON content
Any way to convert this cleanly?
You probably need to wrap your JSON string in curly braces. You generally need a single top-level array or object to have valid JSON. Don't know about NewtonSoft, but using the JavascriptSerializer, this works:
var json = "{\"IsPublisherFor\":[\"p56\",\"p124\",\"p258\"]}";
var serializer = new JavaScriptSerializer();
stuff obj = serializer.Deserialize<stuff>(json);
With the following class defined to receive the data:
public class stuff
{
public string[] IsPublisherFor { get; set; }
}
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;
}
The angular app I am working on has several large input forms which span several pages. The data is added to an angular model which is a javascript object literal and sent to a webApi controller. The webApi parameter for my POST methods is a C# class (duh) with a lot of properties! Is there a utility which will generate the javascript from my C# class, so that my binding just works! I've googled this and failed even though it seems such a mundane task. As always thanks in advance.
You should use JSON serialize on your JS side and deserialize on your c# side.
JS:
System.Web.Script.Serialization.JavaScriptSerializer serializer =
new System.Web.Script.Serialization.JavaScriptSerializer();
string jsonParam = oSerializer.Serialize(param);
While on your c# side you should use something like, lets say your class is Person:
C#:
Person person = new JavaScriptSerializer().Deserialize<Person>(param);
see msdn documentation on serialize\deserialize json object
I'm working with the mvc 4 web api to build a service layer that will always return JSON.
My api method calls actually call another service which returns a JSON object. I then want to just pass this JSON object back as my return object, but I'm not sure what return type to use.
If I use string, it wraps the JSON object in quotes.
By the way, I already changed the default MediaTypeFormatter to be JSON.
Here is an example of the JSON object:
{
"responseHeader":{
"status":0,
"QTime":0,
"params":{
"indent":"on",
"q":"id:100001",
"wt":"json"}},
"response":{"numFound":1,"start":0,"docs":[
{
"Header":"Test Header",
"MaxPrice":515.0,
"ApartmentName":"Apartment 1",
"MaxBathrooms":4.0,
"Pool":true,
"MinBathrooms":2.0,
"MaxBedrooms":4,
"CoveredParking":false}]
}}
In the Beta release, you can use JsonValue (from the System.Json namespace). If your call to the other service returns a string which contains the JSON data, then you can call JsonValue.Parse to load that into the object to return.
In the RC release (or in the current bits from http://aspnetwebstack.codeplex.com) you can use the JToken object (from the Newtonsoft.Json.Linq namespace) - the default JSON serializer and JSON DOM are now coming from the JSON.NET library.
So, the issue turned out to be that I was taking a serialized json object and then reserializing in the JavaScriptSerializerFormatter that overrides the default xml MediaTypeFormatter.
I fixed the issue by returning the JsonValue and then checking the type and not reserializing it.
Thanks carlosfigueira for the help finding this.
To return Json from MVC, use JsonResult. You can first convert the string to an object using DataContractJsonSerializer.
http://shashankshetty.wordpress.com/2009/03/04/using-jsonresult-with-jquery-in-aspnet-mvc/
i have a method that returns a list of serialized objects in my page code behind
private string Get()
{
JavaScriptSerializer jsonSerialiser = new JavaScriptSerializer();
string jsonString = SerializeToJason(UnavailabilityBusiness.Get(new DateTime()));
return jsonString ;
}
i would like to pass this serialized objects to javascript
function getJson()
{
??????
}
How do I do it?
You could write them out as a script, typically leveraging ClientScript.
You would want to take care to register the script so that it renders before the consuming script.
Looks like this guy wrote a utility class to do what you're trying to do (Code Project Link).
I have't really looked at it but you can get his source and have a look.
Good luck,
Patrick.