I have a controller action that is receiving a complex object via JSON. The object has a property on it that is declared as an abstract type. Currently, the action method never gets executed because when the JSON object is deserialized it chokes on the abstract type.
How can I customize the deserialization phase so that I can supply the correct concrete types to the deserializer?
public class ComplexType
{
public AbstractClass AbstractObject { get; set; }
}
public abstract class AbstractClass
{
}
public class ConcreteClass1 : AbstractClass
{
}
public class ConcreteClass2 : AbstractClass
{
}
[AcceptVerbs(HttpVerbs.Post)]
public JsonResult MyAction(ComplexType complexObject)
{
// code ...
return Json(myResult);
}
Called with:
$.ajax({
url: myUrl,
data: JSON.stringify(instanceOfComplexType),
cache: false,
contentType: "application/json",
complete: function (data, textStatus) {
// handle response
},
dataType: "json",
type: "POST",
processData: false
});
In the end I wrote an ActionFilter that processes the incoming JSON using JSON.NET and a custom Converter. The custom Converter is smart enough to decide using the JSON data which of my derived classes it should instantiate.
An Recieving class has allways to have concrete implementations.
otherwise, the deserializer cannot instantiate the objects.
and there is no other out there that solves you that.
you have 2 possibilities.
either remove the abstract from the base class,
or make a implementation of the complexType that implements a concrete AbstractObject property (maybe through contravariance or generics)
Related
It seems things have become more complex going from ASP.NET MVC to .NET Core because I can no longer easily send List of objects to controller using Ajax. Am I doing something wrong?
In my controller, I have this method:
[HttpPost("EditMultipleResults")]
[Consumes("application/x-www-form-urlencoded")]
public bool EditMultipleResults([FromForm] List<Result>, [FromForm] string comment)
{
// do something...
return true;
}
Result is defined here
public class Result
{
[Key]
public long taskcd { get; set; }
public long Runno { get; set; }
public string Workorder {get; set;}
}
In my JS Ajax I have:
var results = [
{taskcd: 123,
Runno: 187776876,
Workorder: 'VA1234567'
},
{taskcd: 642,
Runno: 187776877,
Workorder: 'VA1234569'
},
{taskcd: 766,
Runno: 187776876,
Workorder: 'VA1234564'
}
];
var posteddata = {
results: results,
comment: 'test comment'
};
// call the controller
$.ajax({
type: 'POST',
data: posteddata,
traditional: true,
contentType: 'application/x-www-form-urlencoded; charset=utf-8',
url: 'api/ResultsEditor/EditMultipleResults',
success: function () {
deferred.resolve();
},
error: deferred.reject
});
return deferred.promise();
My problem is that results list and reason are null when in controller. How can I pass a list of objects to controller in .NET Core 5?
Another question: is there a way to see the data that's being passed to controller in dev tools?
you can't use [FromForm] (as well as [FromBody]) multiple times, your action input parameter should be the same as it is in ajax, so you will have to create a class
public class PostedData
{
public List<ResultItem> Results { get; set; }
public string Comment { get; set; }
};
and action
public bool EditMultipleResults(PostedData data)
{
// do something...
return true;
}
and you don't need to use [Consumes("application/x-www-form-urlencoded")] ,[FromForm] and contentType: 'application/x-www-form-urlencoded; charset=utf-8' since all of them are already by default.
Because you are creating form data as a json object manually, instead of posting an html form, you need to encode that data as x-www-form-urlencoded manually also.
This post has some great suggestions. Make sure to read not just the accepted answer.
I want to send all the data from the DB as a json array to jsp to be fetched by ajax.
EmployeeController
public class EmployeeController {
#Autowired
private EmployeeService employeeService;
#RequestMapping(value = "/index", method = RequestMethod.GET)
public #ResponseBody List<Employee> listAllUsers() {
return employeeService.listEmployeess();
}
and the jsp
ajaxCall = function() {
$.ajax({
url : 'EmployeeController',
type : 'GET',
dataType : 'json',
error : function(that, e) {
alert(e);
},
success : function(data) {
alert(data);
}
});
}
so how to make this?
By Default your REST Controller converts java objects into JSON object out-of-the Box. But you can also use #Produces("application/json") above the controller method.
Please try to run the ajax response data in a loop data[i], if it doesn't work then use the dot invocation method to reach the data.
I'll start from my problem:
I have a webmethod that I'm calling via AJAX/JSON.
Let's call it "GlobalMethod", that's used to manage a "container" object, that has a list of items derived from the same "baseclass".
This is a code sample of my situation:
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
[System.Web.Script.Services.ScriptService]
public class MyService : System.Web.Services.WebService
{
[WebMethod]
public string GlobalMethod(Container data)
{
string result = "";
foreach (var item in data.Items)
{
result += item.BaseProperty;
if (item is FirstDerivedClass)
result += ((FirstDerivedClass)item).FirstDerivedProperty;
else if (item is SecondDerivedClass)
result += ((SecondDerivedClass)item).SecondDerivedProperty;
}
return result;
}
}
public class Container
{
public List<BaseClass> Items;
}
public abstract class BaseClass
{
public string BaseProperty;
}
public class FirstDerivedClass : BaseClass
{
public string FirstDerivedProperty;
}
public class SecondDerivedClass : BaseClass
{
public string SecondDerivedProperty;
}
This method simply doesn't work out. I won't be able to call this method using the default JavascriptSerializer, as the serializer isn't able to resolve what kind of objects the container will have: they could be of type FirstDerivedClass or SecondDerivedClass.
So, browsing the web for solutions to my problem, I've come across Json.NET, whose method JsonConvert.DeserializeObject is able to retrieve the original type of object that was serialized using the JsonConvert.SerializeObject, since it adds a property called "$type".
My problem now is: how can I make the webmethod using this serializer instead of the default one used by the ASMX?
If the method signature remains
[WebMethod]
public string GlobalMethod(Container data)
then I'll get a totally empty Container object, as the framework is doing the deserialization job and doesn't know which items to instantiate, and I have no way of telling the framework that it should use Json.NET for the job, which would be able to fully deserialize the data.
If I try modifying the method this way
[WebMethod]
public string GlobalMethod(string data)
{
string result = "";
Container container = JsonConvert.DeserializeObject<Container>(data,
new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Objects
});
foreach (var item in container.Items) {
...
then I'll get another server error 500 because "no parameterless constructor defined for type of u0027system.string u0027"
Ok, hopefully my problem is clear... I've been spending the entire day on it and I don't seem to find a solution. Revisiting the architecture of my method in order to avoid usage of derived classes isn't quite an option (beware the actual code is much more complex, I've just simplified it to get to the point!).
By the way, I'll add that the ajax method calling the webmethod is done this way:
$.ajax({
type: "POST",
url: wsUrl + method,
data: JSON.stringify(data),
contentType: "application/json; charset=utf-8",
dataType: "json",
processData: false,
success: function(msg)
{
try{
success(JSON.parse(msg.d));
} finally {
}
},
error: error
});
Thanks to anybody who will share his knowledge with me!
I'd like to share how I actually solved my problem. As I was already saying, I modified my method this way:
[WebMethod]
public string GlobalMethod(string data)
{
string result = "";
Container container = JsonConvert.DeserializeObject<Container>(data,
new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Objects
});
foreach (var item in container.Items) {
Initially, I started receiving the error 500 "no parameterless constructor defined for type of u0027system.string u0027"
But eventually I found out the reason: the json string that was travelling from client to server was being deserialized by the .NET framework into the contained object, which wasn't a string, but an actual JSON object!
So the trick was the following, in my ajax method:
$.ajax({
type: "POST",
url: wsUrl + method,
**data: JSON.stringify(JSON.stringify(data)),**
contentType: "application/json; charset=utf-8",
dataType: "json",
processData: false,
success: function(msg)
{
try{
success(JSON.parse(msg.d));
} finally {
}
},
error: error
});
which means that the json object is encapsulated inside another string, which will be manually deserialized by my server-side GlobalMethod(string), through Json.NET.
Obviously I won't be including this double "stringify" in the ajax routine, but I'll pay attention to pass a JSON.stringified data in input to the ajax routine itself!
This is just an hack,
it's just an idea, doesn't sound elegant but it should work
I'm just curious
change the container in this way:
public class Container
{
public List<MyUnionBaseClass> Items;
}
and define (pseudocode):
public class MyUnionBaseClass{
public void MyUnionBaseClass(BaseClass b){
this.setValue(b);
};
final public BaseClass getValue(){
if(first!=null) return (BaseClass) first;
if(second!=null)return (BaseClass) second;
return null;
}
final public setValue(BaseClass b){
if(b instanceOf firstClass){
first = (FirstClass) b;
}
if(b instanceOf secondClass){
second = (SecondClass) b;
}
}
private FirstDerivedClass first=null;
private SecondDerivedClass second=null;
}
P.s: this is very rought and should be improved.
Does this make sense?
Change the webmethod to accept an object
[WebMethod]
public string GlobalMethod(object data)
This should resolve the issue you are having.
I need to pass a JS object to my controller method jqGrid. The object is call "activeFilters" - here is the object represented as JSON:
{"family":
[{
"filterDisplayName":"Performance Status",
"filterDbName":"CurrentStatus",
"filterValueList":"On Plan"
}]
}
Having problems passing the above to my jqGrid (details further down). But I can pass the object to a controller with Ajax very simply:
$.ajax({
url: myMethodPath,
type: 'POST',
dataType: 'html',
data: JSON.stringify(activeFilters),
contentType: 'application/json; charset=utf-8',
success: function (result) {
alert("success")
},
error: function () {
alert("Error:");
}
});
My test controller method looks like:
[HttpPost]
public ActionResult DataTest(JsonFilterFamily activeFilters)
{
return PartialView();
}
Add the structure of JsonFilterFamily looks like:
public class JsonFilterFamily
{
public List<FilterFamilyMember> family { get; set; }
}
public class FilterFamilyMember
{
public string filterDisplayName { get; set; }
public string filterDbName { get; set; }
public string filterValueList { get; set; }
}
With the above Ajax, the JS object gets sent to the controller without problems. But I just can't figure out how to send the same JS object as the postData in a call to jqGrid controller. I must have the syntax wrong for post data. Here is what I am using:
$("#myJqGrid").setGridParam({ postData: { family: activeFilters.family} }).trigger("reloadGrid");
But when the controller fires, something strange happens. The debugger tells me the count of family = 1; but filterDisplayName, filterDbName, filterValueList are null. Any ideas on what is wrong?
In your controller method, try putting:
var filterFamilyMember = new FilterFamilyMember();
TryUpdateModel(filterFamilyMember);
TryUpdateModel is a MVC controller method and this will put the data in your object.
Background:
I have a MVC2 project and am using jQuery 1.4.2.
I have a .NET class that I put into a view using a JsonResult.
The page then uses jQuery to do stuff which will result in properties of the object put on the page by item (2) above.
When a certain page element is clicked jQuery $.post's the JSON.stringify(myObj) back to action method.
What I'd like to be able to have is something like:
[HttpPost]
public ViewResult MyAction(MyClass minime)
{
}
... MyClass would be the .NET class to deserialize the JSON into which should be fine given it was the type MyClass that got "return Json(MyClass);"
At the present time what I'm seeing is that either the object parameter is null or has no values as set by the JS/jQuery code. If I try:
MyClass foo = new MyClass();
UpdateModel(foo);
It doesn't throw an exception but similar doesn't populate the class either.
Anybody have any ideas on how to solve this? Or how to deal with JSON sent back to the action method and get it into a class.
If you have an Action
[HttpPost]
public JsonResult MyAction(MyClass minime)
where for example
public class MyClass {
public string Name { get; set; }
public int Age { get; set; }
}
you should fill all properties of the class MyClass as a parameters. The input data must not be in JSON format:
$.ajax({
url: '/MyController/MyAction',
data: { name: "Peter", age: 33 },
dataType: 'json',
traditional: true,
type: 'POST',
success: function (data) {
// get data which are already deserialized from JSON
}
});
If you have DateTime properties read JSON Date parameter passed to MVC Action is always null.