I am having a problem deserializing some JSON string back into .net objects. I have a container class which contains some information from external and there is a field call ClassType which defined what type of information is that and the actual content is in another property, which currently can be anything, so we define that as an Object type.
Following are the .net class definition which helps to understand the issue.
class ClassOne
{
public string Name { get; set; }
public int Age { get; set; }
}
class ClassTwo
{
public string AddressLine { get; set; }
public string AddressLine2 { get; set; }
}
class ClassThree
{
public string Country { get; set; }
public string Passport { get; set; }
}
class ContainerClass
{
public string ClassType { get; set; }
public object ClassContent { get; set; }
}
When getting the information from external in a JSON format it will be something like:
{"ClassType":"Class1","ClassContent":{"Name":"James","Age":2}}
I am using Newtonsoft JSON.net library to deserialize the JSON string. It seems like that the default deserialize function will just deserialize that into an Newtonsoft.Json.Linq.JContainer. I just wondering how can I write some Converter to deserialize the ClassContent based on the ClassType definition. Any code sample will be highly appreciated.
I would go dynamic way, like:
string json = #"{""ClassType"":""Class1"",""ClassContent"":{""Name"":""James"",""Age"":2}}";
dynamic jObj = JObject.Parse(json);
if (jObj.ClassType == "Class1")
{
Console.WriteLine("{0} {1}", jObj.ClassContent.Name, jObj.ClassContent.Age);
}
Since returning an object (ClassContent) doesn't mean much, and you have to cast it to a concrete class somehow (using some if's or switch).
Sample:
var container = JsonConvert.DeserializeObject<ContainerClass>(json);
JContainer content = (JContainer)container.ClassContent;
switch(container.ClassType)
{
case "Class1": return container.ToObject(typeof(ClassOne));
..
}
use dynamic and call .ToObject(Type type)
dynamic root = JObject.Parse(json)
return root["ClassContent"].ToObject(Type.GetType(root["ClassType"]))
Try the following
var jsonObject = JObject.Parse(jsonString);
var result = jsonObject.ToObject(Type.GetType("namespace.className"));
Related
I have looked at several solutions over the web on reading nested json files but I haven't found one suitable to my need. Maybe because I am new to JSON. Here is my issue:
I have the following JSON in a file:
{
"ConfigError" : {
"DateSent": "2022-04-28T14:03:16.6628493-07:00",
"ToolType": "WSM",
"IsSent": true
},
"FileCopyError" : {
"DateSent": "2022-06-14T14:03:16.6628493-07:00",
"ToolType": "RMT",
"IsSent": false
}
}
For this I have written two classes. One for the Inner object:
public class SummaryEmailStatus
{
public DateTime DateSent { get; set; }
public string ToolType { get; set; }
public bool IsSent { get; set; }
}
One for the Outer Objects:
public class SummaryEmailClass
{
SummaryEmailStatus Status { get; set; } = new SummaryEmailStatus();
}
I would like to be able to read the JSON in C#. I'm primarily concerned with the inner objects. They are of same class but they need to be used differently. So ideally I'd want a function that I can pass in "ConfigError" or "FileCopyError" into and it will return SummaryEmailStatus class object populated by the values in the JSON:
public static void ReadJasonFile(string jsonFileName, string objctName)
{
List<SummaryEmailClass> emailClassList = new List<SummaryEmailClass>();
dynamic jsonFile = JsonConvert.DeserializeObject(File.ReadAllText(jsonFileName));
SummaryEmailStatus sumclass = jsonFile[objctName];
}
But this gives me a run time error saying:
Cannot implicitly convert type "Newtonsoft.Json.Linq.JObject to SummaryEmailStatus
How can I successfully parse out the inner summaryemailstatus objects?
Additionally, I'd like to be able to create the JSON data within C#. The reason being, when I read the JSON, I will do some task and then will need to update the values of the JSON with the current timestamps. I'd imagine, I'd need to rewrite the file. How can I write a nested JSON like this in C#?
If JSON is not the best way to do this, I am open to alternatives
you can try
string json = File.ReadAllText(jsonFileName);
Dictionary<string,SummaryEmailStatus> summaryEmailStatus =
JsonConvert.DeserializeObject<Dictionary<string,SummaryEmailStatus>>(json);
you can use it
SummaryEmailStatus configError = summaryEmailStatus["ConfigError"];
if you want update data
summaryEmailStatus["ConfigError"].DateSent= DateTime.Now;
and serialize back
json = JsonConvert.SerializeObject(summaryEmailStatus);
or if you have only 2 main properties, create a class
public class SummaryEmailClass
{
SummaryEmailStatus ConfigError { get; set; }
SummaryEmailStatus FileCopyError{ get; set; }
}
and use it
SummaryEmailClass summaryEmailStatus =
JsonConvert.DeserializeObject<SummaryEmailStatusClass>(json);
SummaryEmailStatus configError = summaryEmailStatus.ConfigError;
Summary
You need to convert your JObject into the type you are expecting, as shown here:
SummaryEmailStatus sumclass = jsonFile[objctName].ToObject<SummaryEmailStatus>();
Details
jsonFile[objtName] is of type JObject. The reason is because JsonConvert.DeserializeObject has no idea that you intend to convert that into a list of SummaryEmailStatus.
Once you have your array of JObjects, you can convert that into a SummaryEmailStatus as shown in the following snippet:
public static void ReadJasonFile(string jsonFileName, string objctName)
{
List<SummaryEmailClass> emailClassList = new List<SummaryEmailClass>();
dynamic jsonFile = JsonConvert.DeserializeObject(File.ReadAllText(jsonFileName));
SummaryEmailStatus sumclass = jsonFile[objctName].ToObject<SummaryEmailStatus>();
}
Easy way is kept both objects in JSON, I rewrite your code and add root. For example, if you want to write Config Error and don't write File Copy Error, you can save one of them like null.
public class ConfigError
{
public DateTime DateSent { get; set; }
public string ToolType { get; set; }
public bool IsSent { get; set; }
}
public class FileCopyError
{
public DateTime DateSent { get; set; }
public string ToolType { get; set; }
public bool IsSent { get; set; }
}
public class Root
{
public ConfigError ConfigError { get; set; }
public FileCopyError FileCopyError { get; set; }
}
//in your method to get all data
var json = File.ReadAllText(jsonFileName);
var myDeserializedClass = JsonConvert.DeserializeObject<Root>(json);
Example change config and write to file
var json = #"{
""ConfigError"" : {
""DateSent"": ""2022-04-28T14:03:16.6628493-07:00"",
""ToolType"": ""WSM"",
""IsSent"": true
},
""FileCopyError"" : {
""DateSent"": ""2022-06-14T14:03:16.6628493-07:00"",
""ToolType"": ""RMT"",
""IsSent"": false
}
}";
var conf = JsonConvert.DeserializeObject<Root>(json);
conf.ConfigError.DateSent = DateTime.Now;
conf.ConfigError.ToolType = "New way";
conf.ConfigError.IsSent = false;
conf.FileCopyError = null;
var newJson = JsonConvert.SerializeObject(conf);
File.WriteAllText("your path", newJson);
The service I'm dealing returning different objects nested into a generic object. Here are sample mockup results from service
{"date":1591430887.591481,"results":[{"identity":"result_type_a","result":{"attr_a":1591427634}}]}
{"date":1591430887.591481,"results":[{"identity":"result_type_b","result":{"attr_b":1591427634,"attr_bb":3591457634}}]}
{"date":1591430887.591481,"results":[{"identity":"result_type_c","result":{"attr_c":1591427634,"attr_cc":3591457634,"attr_cc":59634}},{"identity":"result_type_d","result":{"attr_d":"rfrvr","attr_dd":"ytur"}}]}
I tried creating a generic object with declaring result attribute as string. And planning that I could deserialize to the returning json to that generic object, check identity attribute and deserilize the string in result attribute to specific object type.
Here is the object structure
public class GeneralResponse
{
[JsonProperty("date")]
public double Date { get; set; }
[JsonProperty("results")]
public List<GenericResults> Results { get; set; }
}
public class GenericResults
{
[JsonProperty("identity")]
public string Identity { get; set; }
[JsonProperty("result")]
public string Result { get; set; }
}
To serialize/deserialize I'm using Newtonsoft library and the code is below
public static GeneralResponse SerializeResponse(string response)
{
return JsonConvert.DeserializeObject<GeneralResponse>(response);
}
Unfortunately I got following exception while deserializing generic object.
"Unexpected character encountered while parsing value: {. Path 'results[0].result', line 1, position 71."
If I declare Result property of GenericResult as object as below
public class GenericResults
{
[JsonProperty("identity")]
public string Identity { get; set; }
[JsonProperty("result")]
public object Result { get; set; }
}
I can pass first serialization and make the second serialization without getting any exception.
string inner_object = response.Result.ToString();
switch (type)
{
case ResponseTypes.type_A: return JsonConvert.DeserializeObject<ObjectTypeA>(inner_object);
case ResponseTypes.type_B: return JsonConvert.DeserializeObject<ObjectTypeB>(inner_object);
case ResponseTypes.type_C: return JsonConvert.DeserializeObject<ObjectTypeC>(inner_object);
default: throw new Exception("Unknown Response Type");
}
But returned object does not contain the data.
I would appreciate any help about modeling this algorithm. Thanks in advance.
When you use string with JSON.NET serialization it is not actually using the JSON string but tries to parse a JSON string value.
You could use JObject instead, which is JSON.NETs wrapper for JSON objects. The JObject can then be used to deserialize into a type or by used to access JSON properties directly. Like this:
switch (type)
{
case ResponseTypes.type_A: return response.Result.ToObject<ObjectTypeA>(inner_object);
case ResponseTypes.type_B: return response.Result.ToObject<ObjectTypeB>(inner_object);
case ResponseTypes.type_C: return response.Result.ToObject<ObjectTypeC>(inner_object);
default: throw new Exception("Unknown Response Type");
}
You could use a Dictionary to store the result, which looks to be polymorphic. For example,
public partial class GeneralResponse
{
[JsonProperty("date")]
public double Date { get; set; }
[JsonProperty("results")]
public ResultElement[] Results { get; set; }
}
public partial class ResultElement
{
[JsonProperty("identity")]
public string Identity { get; set; }
[JsonProperty("result")]
public Dictionary<string,object> Result { get; set; }
}
Working Demo
Output Sample
I am requesting a JSON from a standard web service and I need to handle the response so I can work with the objects. I am working in Xamarin Studio - not that i think that matters.
You can see a result from web service by:
https://dawa.aws.dk/vejnavne/autocomplete?q=due
This is requesting street names in Denmark with 'due' in it.
public async Task doAsyncAddress(string input)
{
var template = "https://dawa.aws.dk/vejnavne/autocomplete?q={0}";
var url = string.Format(template, input);
using (var httpClient = new HttpClient())
{
try
{
Task<HttpResponseMessage> getResponse = httpClient.GetAsync(url);
HttpResponseMessage response = await getResponse;
var responseJsonString = await response.Content.ReadAsStringAsync();
/*
I have tried different things here, with with JsonConvert and JObject but neither works.. I have an idea that the son string is in wrong format, with "\n" included and i have tried to remove these, but still without results. I can see the string so I know it is there.. But it is not formatted correctly.
*/
}
catch (Exception ex)
{
string message = ex.Message;
return message;
}
}
}
With the JsonConverter.DeserializeObject i do this:
var adress = JsonConvert.DeserializeObject<List<Address>>(responseJsonString);
where Address:
public class Address
{
public string tekst { get; set; }
public List<Vejnavne> vejnavn
{ get; set; }
}
public class Vejnavne
{
public string href { get; set; }
public string navn { get; set; }
}
and the response is:
"Cannot deserialize the current JSON object (e.g.
{\"name\":\"value\"}) into type
'System.Collections.Generic.List`1[MinEjendom.Vejnavne]' because the
type requires a JSON array (e.g. [1,2,3]) to deserialize
correctly.\nTo fix this error either change the JSON to a JSON array
(e.g. [1,2,3]) or change the deserialized type so that it is a normal
.NET type (e.g. not a primitive type like integer, not a collection
type like an array or List) that can be deserialized from a JSON
object. JsonObjectAttribute can also be added to the type to force it
to deserialize frenter code hereom a JSON object.\nPath
'[0].vejnavn.href', line 5, position 11.”
And with JObject i get:
"Error reading JObject from JsonReader. Current JsonReader item is not
an object: StartArray. Path '', line 1, position 1."
Your C# code is wrong. This is the correct one:
public class Vejnavn
{
public string href { get; set; }
public string navn { get; set; } // not List<Vejnavne> vejnavn
}
public class Address
{
public string tekst { get; set; }
public Vejnavn vejnavn { get; set; }
}
Then call it like this:
var adress = JsonConvert.DeserializeObject<List<Address>>(responseJsonString);
When you've JSON, you are .NET developer and finally - you have to convert JSON to C# class, you should use Edit - > Paste Special -> Paste JSON as classes. This is an awesome tool :)
Your code is wrong. This is the generated class from your JSON :
public class Class1
{
public string tekst { get; set; }
public Vejnavn vejnavn { get; set; }
}
public class Vejnavn
{
public string href { get; set; }
public string navn { get; set; }
}
When you have successfully generated your code, you can rename the class.
I have the following JSON
{
"employee" : {
"property1" : "value1",
"property2" : "value2",
//...
}
}
to a class like
public class employee
{
public string property1{get;set;}
public string property2{get;set;}
//...
}
In my JSON if I need to add property3 then I need to make changes in my class too.
How can I deserialize to a class even though if I change my JSON(adding another property like property3).
The serialize/De-serialize techniques like newtonsoft.json is tightly coupled with the Class.
Is there a better way/tool to deserialize these kind of JSON in portable class in c#?
Newtonsoft is not tightly coupled with strong types. You can deserialize the dynamic types too. See the similar question here (How to read the Json data without knowing the Key value)
You can try .net's JavaScriptSerializer (System.Web.Script.Serialization.JavaScriptSerializer). If some field is added or removed it deserializes object normally.
namespace ConsoleApplication8
{
public class Person
{
public int PersonID { get; set; }
//public string Name { get; set; }
public bool Registered { get; set; }
public string s1 { get; set; }
}
class Program
{
static void Main(string[] args)
{
var s = "{\"PersonID\":1,\"Name\":\"Name1\",\"Registered\":true}";
var serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
var o = serializer.Deserialize<Person>(s);
;
}
}
}
If we can use " Dictionary<string,string> employee" the above json can be deserilized.
I have a JSON data as follows
{"id": "367501354973","from": {
"name": "Bret Taylor",
"id": "220439" }
which is returned by an object(result) of IDictionary[String, Object]
In my C# code:
I have made a class for storing the JSON value which is as follows
public class SContent
{
public string id { get; set; }
public string from_name { get; set; }
public string from_id { get; set; }
}
My main C# function which stores the parses the JSON data and stores the value inside the class properties is as follows:
List<object> data = (List<object>)result["data"];
foreach (IDictionary<string, object> content in data)
{
SContent s = new SContent();
s.id = (string)content["id"];
s.from_name = (string)content["from.name"];
s.from_id = (string)content["from.id"];
}
When i execute this code, i get an exception saying System cannot find the Key "from.name" and "from.id"
When i comment the two lines (s.from_name = (string)content["from.name"];s.from_id = (string)content["from.id"];) my code runs fine.
I think i am not able to refer the nested JSON data properly.
Can anyone just validate it and please tell me how to refer nested data in JSON in C#?
Thanks
I'm not sure how you are parsing the JSON string. Are you using a class in the Framework to do the deserialization?
You could use the JavaScriptSerializer Class defined in the System.Web.Script.Serialization Namespace (you may need to add a reference to System.Web.dll)
Using that class, you would write your code like this:
public class SContent
{
public string id { get; set; }
public SFrom from { get; set; }
}
public class SFrom
{
public string name { get; set; }
public string id { get; set; }
}
Then deserialization looks like this:
var json = new JavaScriptSerializer();
var result = json.Deserialize<SContent>(/*...json text or stream...*/);
See JavaScriptSerializer on MSDN. You might also want to check out this similar question.