JSON Deserialization for polymorphic array in MVC 4 controller - asp.net-mvc

Im using MVC 4 my ActionController recives the following Json:
{
"MainId": 1,
"Actions": [
{
"Attribute1ClassA": 1,
"Attribute2ClassA": 2
},
{
"Attribute1ClassB": 3,
"Attribute2ClassB": 4
},
{
"Attribute1ClassC": 5
}
]
}
and the Controller:
[HttpPost]
public ActionResult Commit(ActionsSummaryViewModel summary)
{
//DO stuff
}
and declaration for classes:
public ActionsSummaryViewModel
{
public int MainId {get;set;}
public IList<MainClass> {get;set;}
}
public class MainClass
{
}
public class ClassA : MainClass
{
public int Attribute1ClassA {get;set;}
public string Attribute2ClassA {get;set;}
}
public class ClassB : MainClass
{
public int Attribute1ClassB {get;set;}
public string Attribute2ClassB {get;set;}
}
public class ClassC : MainClass
{
public int Attribute1ClassC {get;set;}
}
So now, how can i manage the deserialization for the MainClass when the action controller receive the JSON ? because when i call the action the list items are null.
if part of the solution is Json.NET, how i can implement for MVC 4 controllers?
Thanks for your help.

You need a property or set of properties from which you can determine which type the class is to use this method. Using JSON.NET, I deserialize the incoming JSON as a dynamic object, then check the common property, determine the type, and deserialize the value again this type using my model type:
// I'm assuming here you've already got your raw JSON stored in 'value'
// In my implementation I'm using the Web API so I use a media formatter,
// but the same principle could be applied to a model binder or however
// else you want to read the value.
dynamic result = JsonConvert.DeserializeObject(value);
switch ((string)result.type)
{
case "typeone":
return JsonConvert.DeserializeObject<ModelOne>(value);
// ...
default: return null;
}
There's a little bit of extra overhead here because you're deserializing twice, but it's worth it in most cases to me because it's easy to understand what's going on and add new types as needed.

You could parse JSON into dynamic object instead using Json.NET:
using Newtonsoft.Json.Linq:
dynamic data = JObject.Parse("{ 'Name': 'Jon Smith', 'Address': { 'City': 'New York', 'State': 'NY' }, 'Age': 42 }");
string name = data.Name;
string address = data.Address.City;

Related

Passing JSON to WEB API Controller

I have this model for an MVC WEB API controller.
What will be the corresponding JSON to match this model structure?
namespace CarEvaluator.Models
{
[DataContract]
public class Record
{
[DataMember]
public List<Boolean> CHits { get; set; }
[DataMember]
public List<Boolean> MHits { get; set; }
}
}
public void Post(Record record)
{
}
Structure:
{"CHits":[true,false],"MHits":[true,false]}
Example:
var postObject = new Object();
// Initialize CHits and MHits as arrays
postObject.CHits = [];
postObject.MHits = [];
// push some items into the arrays
postObject.CHits.push(true);
postObject.CHits.push(false);
postObject.MHits.push(true);
postObject.MHits.push(false);
// serialize data to post
// this is what you set as data property in for instance jquery ajax
$.ajax({
//other params, content type etc
type: 'POST',
data: JSON.stringify(postObject),
...
});
if your parameter is null, you should try to add the [FromBody] attribute and decorate the method with httppost
[HttpPost]
public void Post([FromBody]Record record)
{
}

How to pass a ko.observablearray via JSON to an MVC controller

I'm using Knockout JS to build a model to pass to an MVC controller. The ko.observable() items are passed to the controller no problem, however, the ko.observableArray([]) data is appearing as "count=0" at the controller.
Below is the object I am building in my View:
var AddViewModel = function () {
self.ModelRequest = {
Object: {
VarArray: ko.observableArray([]),
Var1: ko.observable(""),
Var2: ko.observable("")
}
};
....
The ModelRequest.Object.VarArray is an ko.observableArray contains a few attributes in the object: Name, Id, Code, Type.
Below is how I'm sending the data via JSON:
p = ko.toJSON(AddViewModel.ModelRequest);
debugger;
$.ajax({
url: url,
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: ko.toJSON(AddViewModel.ModelRequest),
success: function (data) {
...something...
}
});
When I am debugging the code, I examine the p variable described above and I see the below:
{"Object":{"VarArray":[{"Name":"Name 1", "Id":2, "Code":"50.1", "Type":"A"}],
"Var1":"abc", "Var2":"def"}}
When I examine the object being passed into the controller, Var1 and Var2 have the correct values, however, the VarArray is "Count=0".
Any thoughts? Thanks for taking the time to look at this. I'll try any ideas at this point!!
EDIT 10/6/13:
This is my controller action:
[HttpPost]
public CRUDResponse AddItem(AddRequest ModelRequest)
{
... something here ...
}
At this point when I examine the ModelRequest I see that VarArray is "Count = 0".
Edit 10/8/13:
This is the details of the AddRequest:
#region Public Members
public ObjectType Object { get; set; }
#endregion Public Members
Where the ObjectType is:
#region Public Members
public int Var1 { get; set; }
public int Var2 { get; set; }
public List<SpecType> VarArray { get; set; }
#endregion Public Members
Where the SpecType is
public string Name { get; set; }
public int Id { get; set; }
public string Code { get; set; }
public FieldType Type { get; protected set; }
And the FieldType is a Enum.
UPDATE: I had just found the problem. It looks like the property is not getting serialized properly through JSON when I make a call to my Web API from the UI. The above-mentioned property is of type TypaA which inherits from TypeB. TypeB contains all of the fields needed by TypeA. When I change the property failing to serialize to be of type TypeB, instead of TypeA, it serializes just fine and I get all of the values I need reflected in Web API.
So, basically, JSON fails to serialize a value if it's type is derived from another type. Removing the inheritance by declaring a value to be of base type fixes the issue.
So, is there a way to serialize a property whose type inherits from another class?
Eric
I think the problem is that either A: you are never populating the observableArray, or B: you are not receiving the proper object type back on the controller, either because you are sending it incorrectly or receiving it improperly.
Try doing this -
function testData(name) {
var self = this;
self.Name = ko.observable(name);
}
inside of your view model
var AddViewModel = function () {
self.ModelRequest = {
Object: {
varArray: ko.observableArray([
new testData('Your my boy blue'),
new testData('Frank the tank')
]),
var1: ko.observable(""),
var2: ko.observable("")
}
};
}
And see if your controller action is actually getting your data back.
If not then you are most likely not matching the object you are sending to the controller with an object the controller recognizes.

Changing null strings to string.Empty using ValueInjecter

I'm using ValueInjecter to flatten/unflatten view models into domain objects created by Entity Framework (4.3.1) model-first. All of my VARCHAR columns in my database are NOT NULL DEFAULT '' (personal preference, no desire to open up a holy war here). On post, the view model comes back with any string property that has no value as null, so when I attempt to inject it back into my domain model class, EF barks at me for attempting to set a property with IsNullable=false to null. Example (over-simple):
public class ThingViewModel
{
public int ThingId{get;set;}
public string Name{get;set;}
}
public class Thing
{
public global::System.Int32 ThingId
{
//omitted for brevity
}
[EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=false)]
[DataMemberAttribute()]
public global::System.String Name
{
//omitted for brevity
}
}
Then, my controller post looks like this:
[HttpPost]
public ActionResult Edit(ThingViewModel thing)
{
var dbThing = _thingRepo.GetThing(thing.ThingId);
//if thing.Name is null, this bombs
dbThing.InjectFrom<UnflatLoopValueInjection>(thing);
_thingRepo.Save();
return View(thing);
}
I'm using UnflatLoopValueInjection because I have nested types in the actual domain version of Thing. I attempted to write a custom ConventionInjection to convert null strings to string.Empty, but it appears that UnflatLoopValueInjection switches it back to null. Is there a way I can get ValueInjecter not to do this?
Nuts, I just figured it out with help from the wiki. The solution appears to be to extend UnflatLoopValueInjection:
public class NullStringUnflatLoopValueInjection : UnflatLoopValueInjection<string, string>
{
protected override string SetValue(string sourceValue)
{
return sourceValue ?? string.Empty;
}
}

MVC - Sending array with javascript + ajax to Controller

I'm trying to send an array with coordinates to an MVC controller
I'm doing it like this (not posting all the code, only the relevant):
var coords = [];
..for loop
coords.push({ X: x, Y: y});
..end of loop
then I just do an ajax call with the following object as data
var data = {
OtherData: "SomeString",
OtherData2: 1,
Coords: coords
};
When I debug the action on the controller the other data is parsed correctly
The model I expect looks something like this
public class Model
{
public int OtherData2 { get; set; }
public string OtherData { get; set; }
public Point[] Coords { get; set; }
}
What I already tried
- Using List
- Making a class Simple Point with X and Y as properties
- sending the X and Y values as string values
- sending the X and Y values concatenated as 1 string and receiving a list of string
In the cases of the point object as an array I get a list with the same amount of points but they're all empty (0,0) with the List object the list is just null
any idea?
Maybe an important note is that I'm using MVC4
looks like the DefaultModelBinder doesn't know how to bind to a List,
(it's missing the proper type converter)
what you can do is create your own point and a type converter to it:
[TypeConverter(typeof(PointTypeConverter))]
public class Point
{
public int X { get; set; }
public int Y { get; set; }
}
/// <summary>
/// we need this so we can use the DefaultModelBinder to bind to List<Point>
/// example at http://msdn.microsoft.com/en-us/library/ayybcxe5.aspx
/// </summary>
public class PointTypeConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return true;
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
Point ret = serializer.Deserialize<Point>((string)value);
return ret;
}
}
the controller action will look like this:
[HttpPost]
public ActionResult testPoints(List<Point> cords)
{
//party
}
and the Ajax call like this:
$.ajax({
url: "/Home/testPoints/",
type: "POST",
data: {
//note that i stringify very value in the array by itself
cords: [JSON.stringify({'X':1,'Y':2}),JSON.stringify({'X':3,'Y':4})]
},
success: function (data)
{
//client party
}
});
tested all this in MVC3, and there is no reason it won't work in MVC4
hope this helps

ASP.net MVC - rendering a List containing different types, with a different view for each type

Imagine I have a list of objects that implement an interface called ISummary
The objects within this list MAY have additional properties ie.
public interface ISummary {
Guid Id {get;set;}
string Title {get;set;}
DateTime Created {get;set;}
}
public class GigSummary: ISummary {
Guid Id {get;set;}
string Title {get;set;}
DateTime Created {get;set;}
string VenueName {get;set}
string Band {get;set;}
}
public class NewsSummary: ISummary {
Guid Id {get;set;}
string Title {get;set;}
DateTime Created {get;set;}
string Author{get;set}
}
I now pass this list of Gigs and News Summary objects (as a list of ISummary) to the view as the model.
I want to render this list using a different partial for each type contained in the list.
How can I do this is ASP.NET MVC?
The most obvious way I can think of would be to do something like:
foreach(ISummary summ in listOfISummary) {
Html.RenderPartial(String.Fomat("~/Views/Shared/{0}Renderer.ascx", summ.GetType.ToString()), summ, ViewData);%>
}
and create a strongly typed view with a naming convention, like NewsSummaryRenderer.ascx.
I expect that you could move this out to a helper method though, but I'd add it to one of the existing helpers through an extension method rather than putting it in a code behind as suggested previously.
You could put a helper method in the view's codebehind, and then do something like:
Type modelType = this.Model.GetType();
if (modelType == typeof(NewsSummary)) this.RenderPartial("newspartial", this.Model as NewsSummary);
else if (modelType == typeof(GigSummary)) this.RenderPartial("gigpartial", this.Model as GigSummary);
Lewis is on the right track. I would take a slightly different tack--have both of the "widgets" extend from a common base class which provided information about the view names involved. Then add an extension method to your page class to "render widget" which could get the appropriate view in place.
Check out the Kona ASP.NET MVC sample app for a working example of this concept.
I'd create an HtmlHelper extension that did this. Here's some pseudocode that looks shockingly like c# and may actually work:
public static void TemplatedList<T>(this HtmlHelper me, IEnumerable<T> items,
IDictionary<Type, Action<T>> templates)
{
foreach(var item in items)
{
var template = templates[item.GetType()];
if(template != null) template(item);
}
}
I'd use it like this:
<% HtmlHelper.TemplatedList(ViewData.Model, new Dictionary
{
{typeof(GigSummary), x => %>
<div class="gigSummary">
SUP! GIG ANNOUNCEMENT FOR <%= x.Band %>!!
What: <%= x.Title %>
When: <%= x.Created %>
Who: <%= x.Author %>
</div>
<%}
// add more type/template pairs here
}); %>
Here's a simple extension method you can create to extract just the types you need:
public static class Extensions
{
public static IEnumerable<U> ExtractOfType<U, T>(this IEnumerable<T> list)
where T : class
where U : class
{
foreach (var item in list)
{
if (typeof(U).IsAssignableFrom(item.GetType()))
{
yield return item as U;
}
}
}
}
Test:
public interface IBaseInterface
{
string Foo { get; }
}
public interface IChildInterface : IBaseInterface
{
string Foo2 { get; }
}
public interface IOtherChildIntreface : IBaseInterface
{
string OtherFoo { get; }
}
public class BaseImplementation : IBaseInterface
{
public string Foo { get { return "Foo"; } }
}
public class ChildImplementation : IChildInterface
{
public string Foo2 { get { return "Foo2"; } }
public string Foo { get { return "Foo"; } }
}
public class OtherChildImplementation : IOtherChildIntreface
{
public string OtherFoo { get { return "OtherFoo"; } }
public string Foo { get { return "Foo"; } }
}
....
List<IBaseInterface> b = new List<IBaseInterface>();
b.Add(new BaseImplementation());
b.Add(new ChildImplementation());
b.Add(new OtherChildImplementation());
b.Add(new OtherChildImplementation());
foreach (var s in b.ExtractOfType<IOtherChildIntreface, IBaseInterface>())
{
Console.WriteLine(s.GetType().Name);
}
This will get all of the items in the list that are of the derived type you're looking for. So, in your controller, pass in the entire list to the view. Then, have partial views that take IEnumerable's of the type that partial needs, and within your main view, call this extension method and pass on the result to those individual partial views.

Resources