Passing model with dynamic json object to MVC controller - c#

I have a model like so in javascript:
function SomeModel()
{
this.Id = ko.observable();
this.Name = ko.observable();
this.MetaData = {};
}
Then I need to send this to MVC so I can persist it in mongodb, however I dont know how is best to store the MetaData in the C# representation... as the model looks like:
public class SomeModel
{
public Guid Id {get;set;}
public string Name {get;set;}
public object MetaData {get;set;}
}
However it doesnt like binding the json object to the object, I am not sure if I should be using an ExpandoObject or something, as I am not really that fussed about accessing the data within the c# mvc service, it is only for plugins in the front end which can store their own data on an object for use later. So an example could be:
var someModel = new SomeModel();
someModel.MetaData.Bookmarks = [];
someModel.MetaData.Bookmarks.push({ location: 120, document: 23003032 });
So it could contain anything and sub objects, and like I say I just need to bung it into an object just as a transport mechanism for Mongo, which will save it fine as its schema-less.
Currently the models from JS are sent over ajax via Jquery, and auto binded in the controller then sent off to the repository.
== Edit ==
I have done a few minor tests with MVC4, and I can put an expando object within my models and have it populated, however it only seems to store 1 layer of data, so if I were to postback:
...,
MetaData: {
something: {
one: 1,
two: 2
}
},
...
I would end up with my expando object for the MetaData field knowing about the something field, but would not store any of the data below that first traversal, so is there any way to store the above other than turning it into a string of json to be stored?

As there seemed to be no solid answers I just had to do an extra step in the jquery ajax to convert all "metadata" fields into a json string, then store it as a string on the server.
Then if I need to access or change the data I use the web helper Json.Encode/Decode which returns a dynamic, then I can just add child elements which I do as:
Anonymous type for simple included json object
List for an array element
Then I just encode it again casting the dynamic as an object.

Related

How to cast Azure DocumentDB Document class to my POCO class?

Is there a way to cast the Microsoft.Azure.Documents.Document object to my class type?
I've written an Azure Function class, with a CosmosDBTrigger. The trigger receives an array of Microsoft.Azure.Documents.Document. I like having that Document class so that I can access the meta data about the record itself, but I also would like to interact with my data from my class type in a static way.
I see the JSON representation of my data when I call ToString. Should I manually convert that JSON to my class type using Newtonsoft?
If you need to map your Document to your POCO in the function then the easiest way to do that is what you suggested.
Call the document.Resource.ToString() method and use DeserializeObject from JSON.NET or the json library you prefer. JSON.NET is recommended however as Microsoft's CosmosDB libraries use it as well.
Your mapping call will look like this:
var yourPoco = JsonConvert.DeserializeObject<YourPocoType>(document.Resource.ToString())
While solution offered by Nick Chapsas works, I would like to offer a few better options.
Preferred solution - improve your model
First, if you are interested in the extra meta fields then you can always include the chosen properties into your data access model and they will be filled in. for example:
public class Model
{
public String id { get; set; }
public String _etag { get; set; }
//etc.
}
Then you can use the existing API for deserializing thats explicit and familiar to all. For example:
var explicitResult = await client.ReadDocumentAsync<Model>(documentUri);
Model explicitModel = explicitResult.Document;
If you want the next layer model (ex: domain model) to NOT have those storage-specific meta fields then you need to transform to another model, but that is no longer a cosmosDB-level issue and there are plenty of generic mappers to convert between POCOs.
This is the IMHO cleanest and recommended way to handing data access in cosmosDB if you work on strongly typed document models.
Alternative: dynamic
Another trick is to use dynamic as the intermediate casting step. This is short and elegant in a way, but personally using dynamic always feels a bit dirty:
var documentResult = await client.ReadDocumentAsync(documentUri);
Model dynamicModel = (dynamic)documentResult.Resource;
Alternative: read JObject
Another alternative is to read the document as NewtonSoft's JObject. This would also include all the meta fields and you could cast it further yourself without all the extra hopping between string representations. Example:
var jObjectResult = await client.ReadDocumentAsync<JObject>(documentUri);
Model JObjectResult = jObjectResult.Document.ToObject<Model>();
Alternative: Document + JObject at the same time
Should you really-really want to avoid the document level meta fields in model AND still access them then you could use a little reflection trick to get the JObject from the Document instance:
var documentResult = await client.ReadDocumentAsync(documentUri);
Document documentModel = documentResult.Resource;
var propertyBagMember = documentResult.Resource.GetType()
.GetField("propertyBag", BindingFlags.NonPublic| BindingFlags.Instance);
Model reflectionModel = ((JObject)propertyBagMember.GetValue(documentResult.Resource))
.ToObject<Model>();
Beware that the reflection trick is relying on the internal implementation details and it is not subject to backwards compatibility guarantees by library authors.
You can simply do a .ToString() in the Microsoft.Azure.Documents.Document class.
This class inherits from the Microsoft.Azure.Documents.JsonSerializable class that overrides the .ToString() method.
Here below is an example of deserializing the Document class to my Car.cs POCO using the new high-performant System.Text.Json Namespace:
Car car = JsonSerializer.Deserialize<Car>(document.ToString());

How to properly pass and receive a json object?

From the client, I would like to pass a collection of json nodes: [{key, value},{key, value}] to a WebAPI endpoint. what should my api endpoint param type be? a List<>() or something else?
This would be using C#.
I need to iterate over each endpoint that is passed in the collection.
In general, it is good practice to create a model that your JSON will be deserialized into. To answer your question though if your JSON was in the format
[{key1: value1},{key2: value2}]
you would be able to use a
List<Dictionary<string,object>>()
If you were sure that your values were always string values you could do
List<Dictionary<string,string>>()
As JSON values can be strings (wrapped in quotes), integers (no quotes) or null.
So your Web API controller could be something like this:
[HttpPost]
public IHttpActionResult ReceiveJSON([FromBody]List<Dictionary<string,string>> in_json)
{
// And then one way to iterate over each 'json node' passed
foreach(var dict in in_json)
{
// Do something with dictionary object
}
return Ok(in_sjon);
}
What version of ASP.Net Web API will you be using?

Convert FormCollection to object of type known only at runtime

I have controller with action containing code, which looks like
public ActionResult Index(FormCollection data)
{
var type = findTypeByName(data["CastToTypeName"]);
var model = transformRequestDataToType(data/* or may be Request*/, type) as DbEntity;
SaveData(model);
...
}
Is there any way to transform request data to object of runtime-known type?
I post data to controller with <form> and trying to find solution without changing form data to json.
First of all, I simplified input names notation to such as:
a.name
a.items\
a.items/name
a.items/desc
a.items\
a.items/name
a.items/desc
was
a.name
a.items.Index
a.items[0].name
a.items[0].desc
a.items.Index
a.items[1].name
a.items[1].desc
next I extract form data before submit using js, prevent form submit and send ajax post request with the form data to url defined in action form attribute. On server side I parse data and recursively assign it to properties of instance of given type. So I can ParseAs<MyType>(Request) or even ParseAsGiven(Request) when CastToTypeName value is set.

How to explicitly call the ASP.NET json serializer

So I have a small asp.net app which returns Json objects that are serialized from C# objects. If I just create a function:
[HttpGet(getTheObj)]
public SomeObj GetTheObject()
{
return new SomeObj() { SomeProperty = 1 };
}
Then it works fine and I can do an HttpRequest for the Json object. However I also want to save some these serialized objects into a database for later use. So I'm wondering, can I explicitly call the Json serializer? I understand that several different serializers can be used with ASP.NET, how do I figure out which one I am using (I didn't create the project).
string json = JsonConvert.SerializeObject(_data.ToArray());
you can save this in database. you can again retrive this object from database and deserialize this object.

Working with JSON on the Server-Side in ASP.NET and C#

I have an ASP.NET web form that is using JQuery on the client-side. I have a user interface that is building a collection of objects and storing them in JSON. With my client-side implementation complete, I need to now work with that information when the user clicks a button. When this button is clicked, I need to loop through that JSON collection and validate the entries. My problem is, I'm not sure how to do it.
// Client-Side Code
var myCollection = {
"data": [
]
};
// Server-Side Code
protected void myButton_Click(object sender, EventArgs e)
{
// Parse JSON
}
Each item in the collection is stored in the "data" property. How do I loop through the JSON collection on the server-side? I thought about putting the JSON data in a Hidden HTML Element, but this didn't sound good and I could think of a good way to do it.
Thank you
How you send it to the server is up to you - a hidden field, an AJAX call, whatever you prefer. Once you've got the string on the server, you'll need 2 things:
A C# server-side representation
of that object
A converter to go
from JSON to that C# representation.
Let's adjust your example a bit, because "myCollection" in your example is an object, not a collection. So we'll call it myObject. Secondly, we'll assume that "data" is an array of strings. It could be anything, but we'll keep it simple.
var myObject = {
data: ["string1","string2"]
};
We'll also assume you're using the DataContractJsonSerializer, so you can easily map the two different case-styles...JavaScript is typically camelCase, and C# is typically ProperCase. So, in C#, this would be:
[DataContract(Name="myObjectType")]
public class MyObjectType{
[DataMember(Name="data")]
public string[] Data { get; set; }
}
Now you have two representations of the same structure, one in c#, one in JavaScript. To convert from one to the other, we can use the built-in DataContractJsonSerializer, like this:
public static T Deserialize<T>(string json)
{
using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(json)))
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
return (T)serializer.ReadObject(ms);
}
}
...resulting in a final call of:
MyObjectType myObject = Deserialize<MyObjectType>(incomingString);
JSON in the Hidden Field is a valid way to do it, as the data would then be posted to the server. You could then use the System.Web.Script.Serialization.JavaScriptSerializer component to deserialize the data (to a dictionary) and access the data that way. Not 100% sure hhow array data comes out of that process. THere are also other tools like JSON.NET too to parse JSON.
Another way is via a web service call, but that doesn't go through the page lifecycle.
HTH.

Categories

Resources