I have the following JSON when user clicks save
tasks : {
updated : [
{ Id : 123, SomeField1 : 'new value', SomeField2 : 'new value', SomeField3 : 'new value' },
{ Id : 125, SomeField2 : 'new value' },
{ Id : 127, SomeField1 : 'new value', SomeField4 : 'new value' },
{ Id : 129, SomeField2 : 'new value', SomeField3 : 'new value' },
{ ................ }
],
removed : [
{ Id : 345 },
{ Id : 847 }
]
}
on the MVC server side (C#), I have a ViewModel and .NET deserializes this back to my viewmodel object.
in this example, This object has Id, SomeField1, SomeField2, SomeField3, SomeField4.
The problem I am having is that the client only sends the fields which were actually updated, so If the user never updated SomeField3 it wont be in the json and .NET for that array object will have a null as SomeeField3 ...
so i cant get record, update all the fields to what the viewmodel is and then call an update as it will set SomeField3 to null , which is not correct - there could be data in that field which the user just didn't touch in this case .. (in another case they may have deleted their text, which then the update would be valid..
I am not sure what is the best way to tackle this problem.
Looking forward to your suggestions.
I suggest you to post updated string in API action, then you can get your solution as :
Create dynamic property mapping function :
public static class DynamicToStatic
{
public static T ToStatic<T>(object source, T destination)
{
var entity = destination;
//source implements dictionary
var properties = source as IDictionary<string, object>;
if (properties == null)
return entity;
foreach (var entry in properties)
{
var propertyInfo = entity.GetType().GetProperty(entry.Key);
if (propertyInfo != null && entry.Value != null)//Check property and its values exist or not ,change only when source contains value
propertyInfo.SetValue(entity, entry.Value, null);
}
return entity;
}
}
Convert your request json to dynamic object and then map dynamic object to Your static class type model, Class type model initialized from your db record or any source as per your requirement.
//updatedJsonObjectString bound from request post data(JSONSTRINGIFY of post data)
dynamic source = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(updatedJsonObjectString);
Class1 model = new Class1();//mapped/filled by data call
var retUser = DynamicToStatic.ToStatic<Class1>(source, model);
if you are using Newton Json for Deserializing.
Newton Json DeserializeObject method has an overload which takes json string and JsonSerializerSettings as parameters. JsonSerializerSettings has NullValueHandling and MissingMemberHandling properties.
MissingMemberHandling: Gets or sets how missing members (e.g. JSON contains a property that isn't a member on the object) are handled
during deserialization.
NullValueHandling: Gets or sets how null values are handled during serialization and deserialization
Related
Is there another way to deserialize the response content of an api call into a generic object in .Net without having to create a model representing the json object? In other words, is there or does .Net provide a generic object or model that i can use to deserialize the response into. The reason being is because every api call response is not the same which means i have to create a new model for every api call. And i have about 20 different endpoints that return different responses, which means i would have to create 20 models representing the response.
My recommendation would be to create request and response models for this, even though it feels like extra work. This is because you'll need to eventually pass the parameters to functions in your business layer anyway, and you'll want to take advantage of the type safety without doing a bunch of Int32.TryParse's, which at that point, you're creating variables and doing extra work anyway. Actually, you're not going to be able to outrun the type safety the language not only provides, but generally requires. What I've done, is just copy/paste my DTO or EDMX table model into the new class, then decorate it. Pretty fast.
You may be looking for dynamic object which is late bound with unknown compile time properties.
A handy implementation of this is the JObject available with Newtonsoft.Json package and it offers similar functionality to the typeless elements of Javascript in that the properties can be of any type, with any nesting, etc. as long as it was parsed from well formed Json.
Usage is super simple, but watch out for null values, etc as default behavior for dynamic (aka ExpandoObject under the hood) is to throw exceptions for properties (aka Keys) not found...
public static void Run()
{
string apiJsonResponse = #"{
Name: 'Luke Skywalker',
Title: 'Jedi',
Skills : [
'Lightsaber',
'The Force'
]
}";
dynamic json = JObject.Parse(apiJsonResponse);
Console.WriteLine($"Name: {json.Name}");
Console.WriteLine($"Title: {json.Name}");
Console.WriteLine($"Skills: {String.Join(", ", json.Skills)}");
}
The result will be the Json dynamically parsed and rendered without any strongly typed model:
You can use generic method to have list of JObject. Based on your need you can extract model from jobject.
private static async Task<List<JObject>> CallApi(Uri uri, HttpContent data = null, string headerKey = null, string headerKeyVal = null)
{
string res = string.Empty;
try
{
var handler = new HttpClientHandler
{
//UseDefaultCredentials = true,
};
using var client = new HttpClient(handler);
if (!string.IsNullOrWhiteSpace(headerKey))
client.DefaultRequestHeaders.Add(headerKey, headerKeyVal);
var post = await client.GetAsync(uri);
//var post = await client.PostAsync(uri);
if (post.StatusCode != HttpStatusCode.InternalServerError)
{
res = await post.Content.ReadAsStringAsync();
}
return JsonConvert.DeserializeObject<List<JObject>>(res);
}
catch (Exception ex)
{
throw ex;
}
}
in .Net 3.1 after version, can use using System.Text.Json
api response data
{
"Status" : "0"
"Data" : {
"FirstName" : "firstName",
"LastName" : "lastName"
}
}
Response Model
public class ResponseModel
{
public string Status { get; set; }
public JsonElement Data { get; set; }
}
Deserialize
public void Deserialize()
{
var jsonString = "{ \"Status\": \"0\", \"Data\": { \"FirstName\": \"firstName\", \"LastName\" : \"last_name\"} }";
var response = JsonSerializer.Deserialize<ResponseModel>(jsonString);
//get first_name
var firstName = response.Data.GetProperty("FirstName").GetString();
Console.WriteLine(firstName);
}
I'm following the Create a web API with ASP.NET Core and MongoDB Tutorial
And I'm missing something very basic, how to serialize a Json object that has a property that could be "multi-type". That is, this property could be a string, or another object.
This is the original Json sample in the tutorial:
{
"_id" : ObjectId("5bfd996f7b8e48dc15ff215d"),
"Name" : "Design Patterns",
"Author" : "Ralph Johnson"
}
This is the original POCO Model in the tutorial:
public class Book
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
[BsonElement("Name")]
public string BookName { get; set; }
public string Author { get; set; }
}
When I call the "Post" and "Get" action on the WebApi Controller, the original Json model is serialized correctly into an instance of Book, saved into the database and retrieved correctly from the web service as expected.
But, I need to add a "MultiUse" property, where MultiUse could either be a string or an object, or something else, for example:
{
"_id" : ObjectId("5bfd996f7b8e48dc15ff215d"),
"Name" : "Design Patterns",
"Author" : "Ralph Johnson",
"MultiUse": "Some String"
}
or
{
"_id" : ObjectId("5bfd996f7b8e48dc15ff215d"),
"Name" : "Design Patterns",
"Author" : "Ralph Johnson",
"MultiUse": {
"foo":"bar"
}
}
If I try to Post an object, this MultiUse property is serialized as another .Net type.
What type should MultiUse be in the POCO Model? I've tried Object, Dynamic, BsonDocument, but it is clear I think that AspNet and MongoDb driver will not automatically serialize it as I need it.
This multi-value property is pretty much the only reason I want to use MongoDB, I just want to save whatever is in that property into the database.
This situation works without problems in ExpressJS, but I need .Net because of client requirements.
Of course, I can just drop the strongly typed schema and use the MongoDB net driver functions to insert, update and query without the models. But since this "should" be a common scenario I just wanted to know how is this being handled.
There are several ways to solve this problem,
my suggestion is to import Newtonsoft.Json, then use MultiUse as JObject, or if MultiUse is array as Jarray.
I need to be able to get data from JSON using JSON.NET.
{
"labels": {
"start" : {
"action" : "ASK",
"message" : "What do you want to do, {name}?",
"responses" : {
"kill" : "GOTO label3"
}
},
"label3" : {
"action" : "END",
"message" : "Thanks for playing."
}
}
}
This is my C# code: dynamic json = JsonConvert.DeserializeObject(sourcejson);
This puts the JSON into an object that I can access like this, or similar:
Console.WriteLine(json.labels.start.responses.kill);However, I need to specify which key to retrieve based on a string, for example, retrieving "kill" based on a C# string with the contents of "kill". If I had another response in responses, I could just change the C# string to the name of the specified key and it would retrieve it. How exactly could I accomplish this?
My preference would be to deserialize into a JObject.
For example: JsonConvert.DeserializeObject<JObject>(sourceJson);
Then, you can reference the keys using standard C# indexers.
var jObj = JsonConvert.DerserializeObject<JObject>(sourceJson);
var kill = jObj["labels"]["start"]["responses"]["kill"]?.ToString();
However, make sure that you have the appropriate null checks in various places as it isn't "type-safe" code.
If you want to continue using a dynamic object you could just replace your print statement with json.labels.start.responses["kill"] as shown below;
[Test]
public void Test()
{
var sourcejson = #"{
""labels"": {
""start"" : {
""action"" : ""ASK"",
""message"" : ""What do you want to do, {name}?"",
""responses"" : {
""kill"" : ""GOTO label3""
}
},
""label3"" : {
""action"" : ""END"",
""message"" : ""Thanks for playing.""
}
}
}";
dynamic json = JsonConvert.DeserializeObject(sourcejson);
Console.WriteLine(json.labels.start.responses["kill"]);
}
Your serialized object will have a Dictionary<string, object> of all attributes and you can access the values by their keys.
I am using MongoDb with C# in a WebApi app. My data does not lend well to mapped data types due to the dynamic nature of the document stored. For example, here is an example of some data. Note the Data section with values that can either be arrays or a single string (Technician):
{
"_id" : "5a59129d16d5c42f7444b83d",
"CreatedDate" : "2018-01-09T20:30:19.455Z",
"Data" : {
"AlcoholTest" : [
{
"Technician" : [
"STT",
"BAT"
],
"TestReason" : "not well"
}
]
}
}
When I attempt to return the data like above, I get this instead:
{
"_id": {
"$oid": "5a59129d16d5c42f7444b83d"
},
"CreatedDate": {
"$date": 1515529819455
},
"Data": {
"AlcoholTest": [
{
"Technician": [
"STT",
"BAT"
],
"TestReason": "drunk"
}
]
}
}
Here is the code I'm using:
public object FindById(string id)
{
var filter = new BsonDocument { { "_id", ObjectId.Parse(id) } };
var result = _collection2.Find(filter);
var note = result.Any() ? result.First() : null;
var json = note.ToJson(new JsonWriterSettings{OutputMode = JsonOutputMode.Strict});
return JObject.Parse(json);
}
I can't just return the note object since Newtonsoft does not know how to convert those $data and $oid into valid types and returns a parsing error.
When I attempted to use MongoDb mapping classes in .NET, this is what my class looked like (I'm not including the BsonClassMap.RegisterClassMap stuff for simplicity):
public class Note
{
public string Id { get; set; }
public DateTime? CreatedDate { get; set; }
public IDictionary<string, IList<IDictionary<string, object>>> Data { get; set; }
}
When I tried this code, the Newtonsoft did not know how to handle the case where the object in IList<IDictionary<string, object>> could be an array or string and saved some rather nasty JArray and JObject data instead.
So here are my questions:
Is there a way to make the above C# code return identical JSON as what is represented in the Mongo database (or what the UI is sending and expecting to get back)?
Or is there a way to map my Data document using .NET types that would allow me to use MongoDb mapping classes that accepts either string values, arrays, or both?
I ended up using a different approach for save vs get. For saving, I converted a generic object to BsonDocument, and saved it as it. This resulted in the data format I was expecting (first data format my above question).
I used a strongly-typed collection to get data from the MongoDB so I could avoid the second data format from my question above. To get the data returned in the correct format, I changed this property from
public IDictionary<string, IList<IDictionary<string, IList<string>>>> Data { get; set; }
to
public IDictionary<string, object> Data { get; set; }
This gave me the correctly formatted data I needed. Having two MongoDb collections for getting vs saving was not ideal, but it eliminated my original hack to parse the dictionary BsonDocument into my strongly-typed POCO.
I have a C# view class such as this:
public class DataObject
{
public int Number { get; set; }
public dynamic Data { get; set; } // <-----
}
being used in an MVC method like this
[HttpPost]
public ActionResult SaveData(DataObject request) {}
The problem is that I want to recieve multiple types of objects in the Data property of the DataObject class.
That is, I want both these to work as valid input json objects.
Type 1
{
Number: 1,
Data: {
Text: "a text"
}
}
Type 2
{
Number: 2,
Data: {
Value: 1,
Options: { 1, 2, 3, 4 }
}
}
Is there a way of doing this with either dynamic objects or some other type of json library magic (just making the property dynamic did nothing)?
All i want to do is store this data in a SQL column nvarchar field and return at a later time (through Entity Framework).
An alternate solution would be to create a view model for each type of input but as there will be 100's of variants to it creating all these views and the corresponding input methods would be cumbersome to maintain.
Adding more details as per comment request: The method is called through Angular.
pub.Save = function (jsonData) {
return $http(
{
method: "POST",
url: baseURL + "/Save",
data: { request: jsonData}, // tried this for string
// data: jsonData, // original way
timeout: 30000
}
)
.then(function (result) {
return result.data;
});
}
At the server side, DTO class must match with the same property name which the payload is carrying.
public class DataObject
{
public string test { get; set; } // <-----
}
So, your save method remains the same:
[HttpPost]
public ActionResult SaveData(DataObject request) {}
The payload json is in the object request.test but its seralized.
Deseralize it using Json Library.
How is it handling multiple different types of variables?
Deseralize it to a dynamic type as:
dynamic obj = JsonConvert.DeserializeObject(request.test, typeof(object));
//Properties within the obj are checked at run time.
if(obj.Text != null) {
//Do your thing
}
if(obj.Value != null) {
//Do your thing
}
if(obj.Options != null) {
//Do your thing
}
By converting the data to a JSON string on the client side I was able to send it to the string property and thus being able to use the same typed view for all objects.
I ended up doing this when saving the object (I'm using angular on the front end), converting the Json object to a string.
entry.Data = angular.toJson(entryData.Data, false);
And then when getting the json string back from MVC I did this to get it back to a real javascript object.
entry.Data = angular.fromJson(entry.Data);
MVC would not accept the JSON object into the text property without making it into a json string first.
Using the above method I am storing data like this in my database:
"{\"Value\":123,\"Currency\":\"EUR\"}"