I am trying to deserialize JSON into an object so I can add it to elastic search. JSON can be of many different object types in the project so I would like the function to be dynamic.
First I am serializing the Data that I get from EF Core context
var serializedObject = JsonConvert.SerializeObject(document, Formatting.None,
new JsonSerializerSettings()
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
});
Next I would like to deserialize to an object. For example if I have
public class EValues
{
public dynamic values { get; set; }
}
var test = JsonConvert.DeserializeObject<EValues>(serializedObject.ToString());
I would like the JSON to be deserialized to the below:
{
"values":{
"StudentId":"60712555-ff1d-4a3e-8c81-08d9c2fc4423",
"Student":{
"Name":"string",
"Country":"string",
"Street":"string"
}
}
}
The serializedObject JSON I am actually trying to deserialize:
{
"StudentId":"60712555-ff1d-4a3e-8c81-08d9c2fc4423",
"Student":{
"Name":"string",
"Country":"string",
"Street":"string"
}
}
You can just do:
var test = new EValues {
values = JsonConvert.DeserializeObject<dynamic>(serializedObject)
};
The JSON that would correspond to EValues would have an extra level of nesting { "values" : {} } not present in your serializedObject JSON.
Related
I want to convert json to a specific object.
String : "{\r\n \"Status\": \"PLANNED\"\r\n}"
I tried Newtonsoft Json namespace but it is returning a null value.
var Json= Newtonsoft.Json.JsonConvert.DeserializeObject<Model Class>(String )
I want the result in Json format so that I can extract the value from Json as "PLANNED" but I am getting a null.
PS :The model class contains two properties , Name (type of string), Value(type of Object)
var s = "{\r\n \"Status\": \"PLANNED\"\r\n}";
var obj = Newtonsoft.Json.JsonConvert.DeserializeObject<StatusModel>(s);
The model you have defined is incorrect.
Your model should be like this:
public class StatusModel
{
public string Status { get; set; }
}
Now value will be extracted to this model and you can access the value like:
var value = obj.Status; //"PLANNED"
JSON Definition
JSON (JavaScript Object Notation) is a lightweight data-interchange format. It is easy for humans to read and write. It is easy for machines to parse and generate. It is based on a subset of the JavaScript Programming Language.
[Source] https://www.json.org/
JSON Newtonsoft
Json.NET is a popular high-performance JSON framework for .NET .
[Source] https://www.newtonsoft.com/json
Problem :
Your are trying to deserialize a json to an object and it's returning a null.
In our context a Deserialization is process which transform a json to an object .
var Result= Newtonsoft.Json.JsonConvert.DeserializeObject<Model Class>(String);
The reason why you have a Null as result is beacause you are deserializing a json to Model, knowing that you Json does not correspond to the Model , this is why the Json need to correspond to the Model so that it can store the information of the Json.
Your Model :
The model may contain some property that are not in the json and vice versa
public class StatusModel
{
public string Status { get; set; }
}
Best Regards .
You can do it like this (using Newtonsoft Framework)
using System;
using Newtonsoft.Json;
{
public class JsonHandler : IJsonHandler
{
public IJsonModel ReadJson(IJsonModel model, StreamReader reader)
{
try
{
string jsonFromFile;
using (reader))
{
jsonFromFile = reader.ReadToEnd();
}
status = JsonConvert.DeserializeObject<model>(jsonFromFile);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
return status;
}
}
}
I deserialize JSON file into my Entity Classes. for simplify, Let's assume these are my classes
public class Result{
...
public List<Sens> senses { get; set; }
}
public class Sens{
...
public Object definition { get; set; }
}
After I deserialize my JSON into a Result Object I insert to database. There is no error and I can insert Result and Senses with no conflict. But when I call Result from the database with LINQ query like this I get the Result object but my senses come null.
Question 1 Why and how can I solve it
var results =(from r in db.Results
where r.headword == Id
select r).ToList();
I try to add my senses to Result like this
foreach (Result item in resultmodel.Results)
{
item.senses = (from s in db.Senses
join r in db.Results on s.res equals r
where r.Id_ == item.Id_
select s).ToList();
}
I can add but when I try to show senses from my view I get
System.Collections.Generic.List`1[System.String]
But I am waiting it would be a string
Question 2 what is wrong with my approach.
There will be no problem if you convert your JSON string to the c# object. But in return, you want to map a string to object. What is that object? What are the properties? C# doesn't know anything about it.
One of my projects, I had a similar issue while writing and reading from Redis Cache server.
I added type definitions while serializing the object and deserialized with these types.
Serialize:
var jsonString = JsonConvert.SerializeObject(item, _settings);
return Encoding.UTF8.GetBytes(jsonString);
Deserialize:
JsonConvert.DeserializeObject<T>(jsonString, _settings);
Settings:
private static readonly JsonSerializerSettings _settings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All,
Formatting = Formatting.None,
NullValueHandling = NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Ignore
}
I hope this helps.
Example code
JToken json = JObject.Parse(
" {\"Url\": \"www.fakeUrl.com\",\"CallId\": 12}");
var poco = mapper.Map<CallData>(json);
Console.WriteLine(json);
Console.WriteLine(poco.Url + " " + poco.CallId);
Simple Model
public class CallData
{
public int CallId { get; set; }
public string Url { get; set; }
}
Output
{ "Url": "www.fakeUrl.com", "CallId": 12 }
www.fakeUrl.com 0
I'm just curious to why Automapper isn't mapping the integer in this JSON object? I know there are alternatives out such as a custom extension for this but I'm wondering why AutoMapper can't do this simple map?
Automapper V7.0.1
I resolved the issue by adding a custom mapping. I still believe this to be an issue with the underlining libraries and will investigate further as this simple primitive mapping shouldn't need extensions.
Mapper
CreateMap<dynamic ,CallData>().ConvertUsing((jo) =>
{
var callData = new CallData();
JsonSerializer serializer = new JsonSerializer();
if(jo != null)
serializer.Populate((JsonReader) jo.CreateReader(), callData);
return callData;
});
Usage
var response =_mapper.Map<dynamic, CallData>(_callData);
My controller method is as follows
public ActionResult Index()
{
Tweets model = null;
var client = new HttpClient();
var task = client.GetAsync("http://localhost:33615/api/product").ContinueWith((t) =>
{
var response = t.Result;
var readtask = response.Content.ReadAsAsync<Tweets>();
readtask.Wait();
model = readtask.Result;
});
task.Wait();
return View(model.Result);
}
The url return the row as follws:
[{"Id":1,"Name":"Honda Civic","Description":"Luxury Model 2013"},{"Id":2,"Name":"Honda Accord","Description":"Deluxe Model 2012"},{"Id":3,"Name":"BMW V6","Description":"V6 Engine Luxury 2013"},{"Id":4,"Name":"Audi A8","Description":"V8 Engine 2013"},{"Id":5,"Name":"Mercedes M3","Description":"Basic Model 2013"}]
My Tweets and tweet class are as follows:
public class Tweets
{
public Tweet[] Result;
}
public class Tweet
{
[JsonProperty("Name")]
public string Name { get; set; }
[JsonProperty("Description")]
public string Description { get; set; }
}
I can't figure it out where i do mistake.it gives me following error:
Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'RestFiddlerTest.Controllers.Tweets' because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly
How to fix this.
I've double checked your code and there are a few issues with it.
First of all, I don't see your logic to deserialize to Json, according to the JSon object, I'm guessing that you're using Newtonsoft.Json?
Then you have a class Tweets which contains an array of Tweet objects.
Now, when you create a small sample, like the following:
var dataToSerialize = new Tweets();
dataToSerialize.Result = new [] { new Tweet { Description = "Desc", Name = "Name" } };
var data = JsonConvert.SerializeObject(dataToSerialize);
The output of this piece of code will be the following:
{"Result":[{"Id":null,"Name":"Name","Description":"Desc"}]}
This is not the same as the output you get from your WebAPI.
Now, when you do use Newtonsoft.Json, when serializing your object, you are probably doing something like this:
const string JSon = "[{\"Id\":1,\"Name\":\"Honda Civic\",\"Description\":\"Luxury Model 2013\"},{\"Id\":2,\"Name\":\"Honda Accord\",\"Description\":\"Deluxe Model 2012\"},{\"Id\":3,\"Name\":\"BMW V6\",\"Description\":\"V6 Engine Luxury 2013\"},{\"Id\":4,\"Name\":\"Audi A8\",\"Description\":\"V8 Engine 2013\"},{\"Id\":5,\"Name\":\"Mercedes M3\",\"Description\":\"Basic Model 2013\"}]";
var data = JsonConvert.DeserializeObject<Tweets>(JSon);
Note: I've put the Json string in a string because I didn't have the time to write a full WebAPI.
Now, this will not work, because the string I input and that's also the one that your controller does return is not a Tweets object (remember you're missing the Result in your JSon string? Instead, this is a array of Tweet objects, so you can change your code like the following:
var data = JsonConvert.DeserializeObject<Tweet>(JSon);
Unfortunately, you will receive the exact same error. So why is that? Because you're trying to deserialize your object into an object of type Tweet while your object is an array of type Tweet.
So, modify your code like the following:
var data = JsonConvert.DeserializeObject<Tweet[]>(JSon);
Now, you're deserializing your object into the correct type.
So, I do hope that this post will help you.
Kind regards,
I used Newtonsoft.Json.
public ActionResult Index()
{
List<Tweet> model = null;
var client = new HttpClient();
var task = client.GetAsync("http://localhost:33615/api/product").ContinueWith((t) =>
{
var response = t.Result;
var readtask = response.Content.ReadAsAsync<List<Tweet>>();
readtask.Wait();
model = readtask.Result;
});
task.Wait();
return View(model);
}
I have an Entity Framework object returned by OData V4 controller.
I return an IQueryable and if I call the OData endpoint without any OData clause I can succesfully do this:
var content = response.Content.ReadAsAsync<IQueryable<Person>>();
And the response in JSON is the following:
{
"#odata.context":"http://xxx:8082/odata/$metadata#Persons","value":[
{
"Id":"291b9f1c-2587-4a35-993e-00033a81f6d5",
"Active":true,
"Alert":"Some alerts for the Person",
"Comments":"Some comments for the Person"
}
]
}
But as soon as I start to play with OData, for example by using $expand on a Complex property I get the following exception:
Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Linq.IQueryable`1[xxx.Person]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.
And the response is the following:
{
"#odata.context":"http://aqavnext01:8082/odata/$metadata#Persons","value":[
{
"Id":"291b9f1c-2587-4a35-993e-00033a81f6d5",
"Active":true,
"Alert":"Some alerts for the Person",
"Comments":"Some comments for the Person",
"Party":{
"Id":"291b9f1c-2587-4a35-993e-00033a81f6d5"
}
}
]
}
And I am deserializing using the same object returned by my Web Api so I don't understand why it fails.
Same issue when I apply $select.
Try deserialize the content like this:
var content = response.Content.ReadAsAsync<ODataResponse<Person>>();
Where ODataResponse is:
internal class ODataResponse<T>
{
public T[] Value { get; set; }
}
If you need access to the #odata.xxx fields in the response JSON (such as implementing a loop for paged results), the following is my implementation which expands on the solution from andygjp.
I'm using RestSharp as my HTTP client and Json.NET for (de)serialization. Steps as follows.
Implement a custom IRestSerializer that uses Json.NET to replace the default RestSharp serializer. Below example implementation:
public class JsonNetSerializer : IRestSerializer
{
private readonly JsonSerializerSettings _settings;
public JsonNetSerializer()
{
_settings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore
};
_settings.Converters.Add(new StringEnumConverter());
}
public string Serialize(Parameter parameter) => JsonConvert.SerializeObject(parameter.Value, Formatting.None, _settings);
public string Serialize(object obj) => JsonConvert.SerializeObject(obj, Formatting.None, _settings);
public T Deserialize<T>(IRestResponse response) => JsonConvert.DeserializeObject<T>(response.Content);
public string[] SupportedContentTypes => new string[] { "application/json", "text/json", "text/x-json", "text/javascript", "*+json" };
public DataFormat DataFormat => DataFormat.Json;
public string ContentType { get; set; } = "application/json";
}
Next, define a class that will represent your OData responses. My expanded response class - which includes the #odata.nextLink field - looks as follows.
private class ODataResponse<T>
{
public T[] Value { get; set; }
[JsonProperty("#odata.nextLink")]
public string NextLink { get; set; }
}
Finally, I create an instance of RestClient, setting up the custom serializer previously created:
var client = new RestClient("https://base.url.here/")
.UseSerializer(() => new JsonNetSerializer());
Now when I execute my request, the data in the response object object also contains my OData values.
var response = await client.ExecuteAsync<T>(request);
var nextLink = response.Data.NextLink;
I'm sure this can be done using the standard HttpClient instead of RestSharp, as the real work is done by the serializer. This was just the example implementation I had on hand.