I understand that MVC4 can automatically bind json to type models.
For example, take an HTMLItem model.
In part of a method I retrieve the HTMLItem model data in json format from an external site using HttpRequest and StreamReader. I grab this as a string and then want to pass it into another method that takes HTMLItem as a parameter.
How do I ensure that the receiving method handles this as the type I require (HTMLItem)? It currently doesn't recognise it as such.
I tried assigning the string to the model in the originating method, but the IDE gives me the red squiggly for assigning a string to another type.
I don't want to have to go through the json string and assign each field manually if possible.
Any help, as always, much appreciated. Thanks.
You could deserialize the JSON to your model like:
using System.Web.Script.Serialization;
...
JavaScriptSerializer serializer = new JavaScriptSerializer();
YourModelType model = serializer.Deserialize<YourModelType>(yourJSON);
Related
This question already has answers here:
Setting the default JSON serializer in ASP.NET MVC
(2 answers)
Closed 8 years ago.
I just solved a recent problem where Json.Net allows me to include the 'type' of an object when it serializes it going to the Client. The reason for this is that the class that I'm sending back and forth has a property that is typed as a BaseClass to 4 Subclasses. The great thing now is that my client is showing each one correctly with the type attribute on the Json.
Now I need to send the object back to my Controller, not to an API controller. I've seen a bunch of posts about the api controller using Json.Net as the default JsonFormatter, but this doesn't work for a regular MVC Controller.
So my question is....How can I get my Controller to use the Json.Net Serializer by default? And...will this cause an issue for other items potentially making ajax calls?
I was able to send my json back to my controller as a string and then use JsonConvert to Deserialize my object successfully, so I know it can be done.
Any thoughts are appreciated.
EDIT:
Ok, so I followed the instructions here to change out the JsonValueProviderFactory and that seems to be working, however its not helping the original issue.
The problem is that the JsonFormatter is still the old one and not using Json.Net. So the ProviderFactory is building the dictionary correctly, but when it tries to Deserialize my object, I'm only getting my properties cast as the BaseClass, and not the Derived Class that I'm expecting.
Thoughts on that part?
EDIT:
Simply...how do I create my own JsonNetMediaTypeFormatter in MVC4? I've changed the ProviderFactory, but that only changes how each individual parameter is serialized to its corresponding value. What I need to do is to use Json.Net so that my Parameters that are typed as a Hierarchy is serialized to the correct derived class...not typed as the Baseclass.
Edit - Last:
Ok...so I did verify that if I call an ApiController, it maps the incoming parameter with the correct subclass. So, I can go this route, but I'd really like to find a way to have the same result with my regular Controller.
I had the same issue and resolved it by simply overriding the Json method on my base controller class.
EX:
protected override JsonResult Json(object data, string contentType, System.Text.Encoding contentEncoding, JsonRequestBehavior behavior)
{
var result = new JsonNetResult
{
Data = data,
ContentEncoding = contentEncoding,
JsonRequestBehavior = behavior
};
return result;
}
Usually I send my post request with a custom parameter and a custom return and object using
HttpClientExtension.PostAsJsonAsync<T>
This allows my to call a post method with a custom object.
Now, I want to be able to send my custom object as a parameter and return value to a GET Method.
Lets say my method signature is
[HttpGet]
public MyMethodResponse MyMethod(MyMethodRequest request)
How can I send a request when I have an instance of MyMethodRequest ?
Thanks.
You need to encode MyMethodRequest onto the query string. You can either encode it as separate query string parameters or as a single one. You have handle the encoding yourself on the client side, remembering to URI-encode the parameters. Decoding is done using a custom ModelBinder or TypeConverter respectively. This article shows examples of binding a complex object on the query string.
I have an action that has this signature:
public ActionResult Update(ContactForm contactForm)
ContactForm is a class from an edmx file. No code first. Its EF 5.0.0.
The edmx file is auto generated.
I have a relation from ContactForm to ContactFormField, which causes a navigation property.
I named it "Fields". It is of type
EntityCollection<ContactFormField>
When I have fields (and only then) included in the posting caused by post variables that looks like this:
ContactForm.Fields[0].Name=foo&ContactForm.Fields[1].Name=bar
etc...
I get an InvalidOperationException before the action is even called.
It occurs in the setter of Fields.
The message is:
The EntityCollection has already been initialized. The
InitializeRelatedCollection method should only be called to initialize
a new EntityCollection during deserialization of an object graph.
If I try to have an action that takes Fields separately, adding an argument like
public ActionResult Update(ContactForm contactForm,
IEnumerable<ContactFormField> fields)
...those will be retrieved correctly, but for each field I have yet another list of ContactFieldAlternative. In other words, I need to be able to post sub objects.
How do I get rid of the exception?
The problem here is how the model binder is interpreting your URL string. The model binder doesn't have any logic which allows it to assume that multiple parameters in the URL string represent the same object. Therefore, instead of creating one instance of ContactForm and binding your fields, it's trying to create an instance of ContactForm and bind Fields[0] to that, then trying to create another instance of ContactForm to bind Fields[1] to, which obviously your method signature doesn't accept. Without creating a custom overload to the model binder, you really can't submit your form values with this URL pattern (and shouldn't anyway, imo). The intended method for accepting a complex object through a post is either through form elements or JSON. One of the prime objectives of the ASP.Net MVC framework is the use of restful URLs, which your parameter list in the URL isn't.
I am developing an application using ASP.NET MVC 4 and it involves web API call where the result I am getting for a specific record is a Json string. I am doing this web API call inside an action method of a controller and the result I am getting for such call is in the following form:
result = "{\"fname\":\"John\",\"lname\":\"Doe\",\"empno\":123456,\"dept\":\"IT\"}"
I am new to MVC and Json and would really appreciate any help on how I can extract specific values from this Json string such as the value for dept?
Thanks.
In order to use this as an actual object in your project you will need to deserialize this string. .NET has it's own deserialization or you can check out Json.Net which is pretty simple to figure out. Check out the documentation on JsonConvert.DeserializeObject<>...
ie...
Instructor desInst = new Instructor();
responseContent = rsp.Content.ReadAsStringAsync().Result;
desInst = JsonConvert.DeserializeObject<Instructor>(responseContent);
Short: how does modelbinding pass objects from view to controller?
Long:
First, based on the parameters given by the user through a search form, some objects are retrieved from the database.
These objects are given meta data that are visible(but not defining) to the customer (e.g: naming and pricing of the objects differ from region to region).
Later on in the site, the user can click links that should show details of these objects.
Because these meta data are important for displaying, but not defining, I need to get the previously altered object back in the controller.
When I use the default asp.net mvc modelbinding, the .ToString() method is used. This off course doesn't return a relevant string for recreating the complete object.
I would have figured the ISerializable interface would be involved, but this is not so.
How should I go about to get the desired effect? I can't imagine I'm the first one to be faced with this question, so I guess I'm missing something somewhere...
The default model binding takes form parameters by name and matches them up with the properties of the type specified in the argument list. For example, your model has properties "Price" and "Name", then the form would need to contain inputs with ids/names "Price" and "Name" (I suspect it does a case insensitive match). The binder uses reflection to convert the form values associated with these keys into the appropriate type and assigns it to the properties of a newly created object of the type specified by the parameter (again derived by reflection).
You can actually look at (and download) the source for this at http://www.codeplex.com/aspnet, although you'll have to drill down into the MVC source from there. I'd give a link to the DefaultModelBinder source, but the way they are constructed, I believe the link changes as revisions are introduced.
So, to answer your question, you need to have parameters (could be hidden) on your form that correspond to the properties of the object that you want to recreate. When you POST the form (in the view) to the controller, the binder should reconstitute an object of the specified type using the form parameters. If you need to do translation from the values in the form parameter to the object properties, you'll probably need to implement your own custom model binder.
[EDIT] In response to your second post:
Let's say that we want to have a link back to an action that uses a customized object. We can store the customized object in TempData (or the Session if we need it to last more through more than one postback) with a particular key. We can then construct the action link and provide the key of the object as value to the ActionLink in an anonymous class. This will pass back the key as a Request parameter. In our action we can use the key from this parameter to retrieve the object from TempData.
<%= Html.ActionLink( ViewData["CustomObject1",
"Select",
new { TempDataKey = ViewData["CustomObject1_Key"] }
) %>
public ActionResult Select()
{
Entity custObj = null;
string objKey = Request.Params["TempDataKey"];
if (!string.IsNullOrEmpty(objKey))
{
custObj = (Entity)TempData[objKey];
}
... continue processing
}
#tvanfosson
Thanks for your explanation, but what about links? (no forms involved)
Currently the Html.ActionLink(c=>c.Action(parameter), "label") takes objects as parameter. These have to be translated into URL parts. For this, MVC ALWAYS goes to the .ToString() method. I don't want to serialize my object in the ToString method.
Shouldn't I be able to somehow help the framework serialize my object? Say through the ISerialize interface or something?