Receive nested anonymous objects in ASP.NET MVC controller - c#

I need a ASP.NET MVC controller, which receives anonymous object from JS in JSON to iterate thru its properties. I used to do this, receiving Dictionary<string, object>. But now one of values is Array, and insted of
receivedDictionary[Photos] = [object, object, object]
it gets it as
receivedDictionary[Photos[0]] = object, receivedDictionary[Photos[1]] = object, receivedDictionary[Photos[2]] = object
I get not one dictionary entry with key = Photos and value = array, but many entries with key = Photos[x] and value = object.
How do I get it as one entry in dictionary or is there any better way to get it as dynamic anonymous object and iterate thru its properties just like in JS?
UPD: JSON looks like this:
{"fields":{"TotalFloors":"9","HouseNumber":"10","Photos":[{"id":0,"ParentID":0,"OriginalUrl":"py4s1y3uyqu","OriginalExt":".jpg","ThumbUrl":"2hn04w2lzuu","FormatUrls":{"WH_109_82_Url":"4cwjarqudvo","WH_766_454_Url":"oofm5qo21rr"}},{"id":0,"ParentID":0,"OriginalUrl":"t3csgq20iro","OriginalExt":".jpg","ThumbUrl":"j1uwwburmse","FormatUrls":{"WH_109_82_Url":"gm4qoery1u2","WH_766_454_Url":"a3c20re3g1d"}}],"Details":"Other details"}}
Controller definition:
[HttpPut]
public ActionResult restId(string className, int id, Dictionary<string, object> fields)
{
....
}

The JsonValueProvider used by the DefaultModelBinder seems to be treating array in an odd fashion in this case (based on the source here), so even a dynamic will most likely have the issue. (Don't think you'll have this issue in MVC 6 however)
However, calling the JavascriptSerializer directly (which funny enough is what the default provider uses) produces the results you're after:
var js = new JavaScriptSerializer();
var res = js.DeserializeObject(#"{'TotalFloors':'9','HouseNumber':'10','Photos':[...],'Details':'Other details'}");
To address your issue you could either alter the parameter to a string and run the above code in your action (obviously replacing the JSON string with the parameter), just means the JSON you're submitting from the front end would need to look more like:
// wrapping the JSON object in quotes to stringify it
{ 'fields' : "{ 'TotalFloors': '9', 'HouseNumber': .... }" }
Otherwise you could implement a custom JsonValueProvider like the one proposed here: https://json.codeplex.com/discussions/347099
The custom value provider is probably the cleaner solution.

Related

Property binding in AspNetCore

I'm using:
return RedirectToAction("GetSchedule", new { requirements = preCheckParams.Requirements, weightValues = preCheckParams.WeightValues});
in my aspnetcore app. Next I want to reuse the values I pass to the anonymous object in another action:
public IActionResult GetSchedule(List<string> requirements, Dictionary<string, int> weightValues)
Strangely, the first value gets bound to the List in GetSchedule action, yet the second object, which is a dictionary, is empty. Are there any special rules regarding dictionaries in such cases?
You can't pass classes in the routeValues parameter of RedirectToAction.
RedirectToAction method return value is HTTP 302 (Status302Found) which produces a GET request to the specified action. Which means that all your parameters will be put in URL as query string.
List/Array of strings requirements can be passed in URL since it binds to ?requirements=value1&requirements=value2&.. etc in query string, but anything more complex than that cannot be bound, only primitive values.
You have several options that first come to my mind:
Serialize object to JSON and pass it as a string. This will result in ugly and confusing URL, but it's least painful way.
Use temporary storage if you don't require strictly stateless mechanism. Store before the action and retrieve the dictionary when you enter it.
If you can retrieve weight values from the backend, you may pass some identifier to query by it.

How to update variable columns based on json object from body? (C# web api, LINQ to SQL)

So right now I can update a record using this code:
//POST: api/Cicmpies/testname
[HttpPost("{cmp_name}")]
public async Task<IActionResult> UpdateCicmpy([FromRoute] string cmp_name, [FromBody] Cicmpy cicmpy)
{
var cicmpyUpdated = await
(from c in _context.Cicmpy
where c.CmpName.Equals(cmp_name)
select c).SingleOrDefaultAsync();
cicmpyUpdated.CmpCode = cicmpy.CmpCode;
await _context.SaveChangesAsync();
return Ok(cicmpy);
}
To achieve this I send a json object using Postman like this:
{
"cmpCode": "testcodeupdated333"
}
To the following URL:
http://localhost:54488/api/Cicmpies/testname
This works since I know I'll only have to update "CmpCode" so I can do:
cicmpyUpdated.CmpCode = cicmpy.CmpCode;
What if I don't know what values will have to be updated? So sometimes the json object can contain 1 key-value pair (CmpCode), but sometimes it can contain 4, sometimes all 20, etc... How can I ensure "cicmpyUpdated" will always set all the values from howmany key-value pairs you entered in your json object?
you can do 3 things:
1.- use default values. Give some default values and then update everything
2.- set a maximum of n pairs per object and then send several objects.
3.- keep checking: example: while (more pairs){do whatever}
Generally, the proper way to handle this is to establish a “ViewModel” class for your input parameter on your action method. One option is to give your viewmodel the same properties of your entity and make those viewmodel properties nullable so that if the input doesn’t provide a value you can add a check in your controller to avoid setting the model property if the input viewmodel property is null. Alternatively you could add a custom model binder to set additional properties on your viewmodel that tell you which fields were present in the Json data.

How to deserialize JSON inside a custom claim (from Auth0)?

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; }
}

EF TPH inheritance lost in Web Api JSON

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;
}

Reading JavaScriptSerializer c# back in javascript

In one of my webservice methods I serialized my object calling JavaScriptSerializer.serilize in c#. Now when that returns a string to my javascript I would like to be able to call the properties from the object. I tried, results.d.ID but that did not work. Here is what it returns. Thanks for any help.
JavaScriptSerializer oSerializer = new JavaScriptSerializer();
Inventory item = Inventories.GetInventoryByID(inventoryID);
string jsonObject = oSerializer.Serialize(item);
For example I would like to get the ID out. how would I do that?
{"d":"{\"ID\":\"589652cf-2ccd-49c1-b457-f2793a2a2424\",\"Brand\":{\"ID\":\"b728281b-cf3c-4ee0-ba3d-a3573b886b14\",\"Name\":\"Puma1\",\"ParentBrand\":null,\"BrandChildren\":{\"IsValueCreated\":false,\"Value\":[]}},\"DateAdded\":\"\\/Date(1327695794000)\\/\",\"AddedBy\":{\"ID\":\"d6e1f2e7-f8d1-4809-aadd-4cacd5c2bc43\",\"Email\":\"mojo#yahoo.com\",\"FirstName\":\"maurice\",\"MiddleInitial\":\"l\",\"LastName\":\"bachelor\",\"Address\":\"111 main st\",\"Phone\":\"2162330333\",\"IsAdmin\":true,\"DateJoined\":\"\\/Date(-62135578800000)\\/\",\"HasPurchased\":false,\"AgreeTerms\":true,\"LastPurchaseDate\":null,\"Password\":\"maurice\",\"CompanyName\":\"sneakers101\",\"AllowEmail\":false,\"PurchaseOrders\":{\"IsValueCreated\":false,\"Value\":[]}},\"LastUpdated\":\"\\/Date(1327688594000)\\/\",\"Instock\":true,\"NumberInStock\":12,\"MainPictureUrl\":\"\",\"AlternativePictureUrl\":\"\",\"ThumbNailUrl\":\"\",\"Price\":12.99,\"Like\":0,\"Discount\":1,\"ItemReleaseDate\":\"\\/Date(568011600000)\\/\",\"ItemCondition\":\"Great\",\"Size\":12,\"ItemNumber\":3,\"IsFavorite\":false,\"Details\":\"test Details\",\"Name\":\"Test\"}"}
As Inerdial said you have strange nested JSON value. If data is in format you actually want - use JSON.parse to re-parse values like:
JSON.parse(results.d).ID.
Here's what it's all about: asp.net's ajax d
var jsonObj = theObject; // Assuming it is parsed somehow
var embeddedJsonObj = $.parseJSON( jsonObj.d );
// embeddedJsonObj.ID is the ID you need.
Demo
It looks like your underlying problem is the manually JSON serialization.
Since ASP.NET will automatically JSON serialize the result of your method, your manually serialized jsonObject string is then getting serialized a second time. That's why Alexei's answer works. jQuery deserialized it once, leaving you with a .d property containing a JSON string, and then JSON.parse() deserialized that inner JSON string.
However, that is doubly inefficient because your data is being serialized twice and deserialized twice. Instead, just do this:
[WebMethod]
public Inventory GetInventoryById(inventoryID) {
return item = Inventories.GetInventoryByID(inventoryID);
}
Then, ASP.NET will return JSON that looks like this:
{"d": {"ID": "589652cf-2ccd-49c1-b457-f2793a2a2424", /* the rest of your data here */ }}
Which jQuery (assuming you're using jQuery) will automatically JSON.parse() since ASP.NET responds with an application/json Content-Type, and then you'll be able to index into like results.d.ID.

Categories