json.net Unable to Deserialize to Object List - c#

I am trying to create a JSON file that contains a list of parts for computers.
My Parts Class
namespace Part_Class
{
public class Part_DB : IEnumerable<Part>
{
public List<Part> Parts = new List<Part>();
public IEnumerator<Part> GetEnumerator()
{
return this.Parts.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
public class Model
{
public String Name { get; set; } // E6430, M4600, T4220
public Model(string Name)
{
this.Name = Name;
}
}
public class Category
{
public String Name { get; set; } // E6430, M4600, T4220
public Category(string Name)
{
this.Name = Name;
}
}
public class Part
{
public List<Model> Models = new List<Model>(); //E6420
public string PartNumber { get; set; } //PPHPX, UK717
public string Description { get; set; } // "320GB Hard Drive", "E6410 keyboard"
public List<Category> Categories = new List<Category>(); //Hard Drive, Keyboard
public bool HeroKit { get; set; } //Y or N
}
}
First I create a handful of Model, Categories, and Parts.
....
Model E6410 = new Model("E6410");
Model M4600 = new Model("M4600");
Model M4700 = new Model("M4700");
Category Keyboard = new Category("Keyboard");
Category Hard_Drive = new Category("Hard Drive");
Part PPHPX = new Part();
PPHPX.PartNumber = "PPHPX";
PPHPX.Models.Add(M4600);
PPHPX.Models.Add(M4700);
PPHPX.Description = "320GB Spindle Hard Drive";
PPHPX.Categories.Add(Hard_Drive);
PPHPX.HeroKit = true;
Part UK717 = new Part();
UK717.PartNumber = "UK717";
UK717.Models.Add(E6410);
UK717.Description = "102 Key Non Backlit";
UK717.Categories.Add(Keyboard);
UK717.HeroKit = true;
//I store those parts into a Part_DB Object
Part_DB Stored = new Part_DB();
Stored.Parts.Add(PPHPX);
Stored.Parts.Add(UK717);
//Then take that object and serialize it into a string
string jsonStr = JsonConvert.SerializeObject(Stored, Formatting.Indented);
//Then save it to a file
System.IO.File.WriteAllText(#"C:\****\Parts.json", jsonStr);
....
This outputs the following json file.
[
{
"Models": [
{
"Name": "M4600"
},
{
"Name": "M4700"
}
],
"Categories": [
{
"Name": "Hard Drive"
}
],
"PartNumber": "PPHPX",
"Description": "320GB Spindle Hard Drive",
"HeroKit": true
},
{
"Models": [
{
"Name": "E6410"
}
],
"Categories": [
{
"Name": "Keyboard"
}
],
"PartNumber": "UK717",
"Description": "102 Key Non Backlit",
"HeroKit": true
}
]
I am having trouble doing the reverse. Deserialize the JSON file back into a Part_DB Object. Here is my attempt
List<string> errors = new List<string>();
try
{
//Create a string of the JSON File
string jsonStr;
using (StreamReader file = File.OpenText(#"C:\****\Parts.json"))
{
jsonStr = file.ReadToEnd();
}
// Deserilize object into the Part_DB
Part_DB Stored = JsonConvert.DeserializeObject<Part_DB>(jsonStr,
new JsonSerializerSettings
{
Error = delegate(object senders, Newtonsoft.Json.Serialization.ErrorEventArgs args)
{
errors.Add(args.ErrorContext.Error.Message);
//Debug.WriteLine(args.ErrorContext.Error.Message);
args.ErrorContext.Handled = true;
},
});
}
catch (Exception ex) {
Console.WriteLine(ex);
}

I suspect it has something to do with the somewhat oddness of your top-level model actually inheriting from IEnumerable<T>. I was able to successfully deserialize the file produced into a Part_DB by using the following:
var newObj = JsonConvert.DeserializeObject<List<Part>>( json );
var partDb = new Part_DB();
partDb.Parts.AddRange( newObj );
The json variable contains the contents of the file, which is actually an array of Part objects, not a complete Part_DB object. Then to reconstruct the entire Part_DB you need to take the deserialized array and add it back into the Parts collection of the Part_DB.
If you want to deserialize straight into a Part_DB you're going to have to change your model so that Part_DB isn't inherited from IEnumerable<T>.
public class Part_DB
{
public List<Part> Parts = new List<Part>();
}
Then you can just deserialize directly into that type.
JsonConvert.DeserializeObject<Part_DB>( json );
But it will change your JSON a bit.
{
"Parts": [
{
"Models": [
{ "Name": "M4600" },
{ "Name": "M4700" }
],
"Categories": [
{ "Name": "Hard Drive" }
],
"PartNumber": "PPHPX",
"Description": "320GB Spindle Hard Drive",
"HeroKit": true
},
{
"Models": [
{ "Name": "E6410" }
],
"Categories": [
{ "Name": "Keyboard" }
],
"PartNumber": "UK717",
"Description": "102 Key Non Backlit",
"HeroKit": true
}
]
}

I think that for the Json.NET the Part_DB is just an object (which happens to be enumerable, but that's not important) so it is looking for the JSON that looks more like:
{ "Parts": and here should be your output from serialization}
The output you got from serialization is actually just the serialized List<Part> so try deserializing to it first, create a new Part_DB object and then assign that list to the Parts property.

Related

How can I store the respective key and value for a given set of attributes in a JSON file in a List

I have a JSON file which I want to deserialize. I am very new to JSON and Deserialization in general and have found this quite challenging .
My JSON file structure is as follows ,
{
"SchoolName": "ThisIsTheSchoolName",
"Student": [
{
"Id": "s001",
"Name": "ABC"
},
{
"Id": "s002",
"Name": "CDE"
},
{
"Id": "s003",
"Name": "EFG"
},
{
"Id": "s004",
"Name": "GHI"
},
{
"Id": "s005",
"Name": "IJK"
}
]
}
What I am trying to do is first store the "SchoolName" as a variable ( Which I will need later on in my application) . Secondly I want to iterate through the JSON list "Student" and store every value in "Id" in a List and Every Value in "Name" in another List . I was not able to fathom a way to do this . Here is what I have been able to try so far (Which )
string filePath = #"D:\Projects\Student.json";
string data = File.ReadAllText(filePath);
Console.Write(data);
Console.ReadLine();
dynamic stuList= JsonConvert.DeserializeObject(data);
foreach (var item in stuList)
{
//string Id = item.Id;
//string Name= item.Version;
}
Would really appreciate some help or direction to something that I can get a head start on this
In Visual Studio there is an option called Paste Special using which we can generate the C# side pay load for the json object which will be :
public class Student
{
public string Id { get; set; }
public string Name { get; set; }
}
public class StudentsInformation
{
public string SchoolName { get; set; }
public List<Student> Student { get; set; }
}
and then can be used to deserialize like:
var studentsInformation = JsonConvert.DeserializeObject<StudentsInformation>(data);
and now the studnets list can be directly used :
var schoolName = studentsInformation.SchoolName;
var students = studentsInformtaion.Student;
I think that your best option is to deserialize your JSON to a C# object like explained in Ehsan's answer.
However, if you don't want to create a C# object and just use the raw info, I suggest you to use a good json library like Json.NET, which allows you to query your JSON easily:
internal static void GetInfoFromJson()
{
string json = #"{
""SchoolName"": ""ThisIsTheSchoolName"",
""Student"": [
{
""Id"": ""s001"",
""Name"": ""ABC""
},
{
""Id"": ""s002"",
""Name"": ""CDE""
},
{
""Id"": ""s003"",
""Name"": ""EFG""
},
{
""Id"": ""s004"",
""Name"": ""GHI""
},
{
""Id"": ""s005"",
""Name"": ""IJK""
}
]
}";
JToken data = JObject.Parse(json);
var schoolName = data["SchoolName"];
foreach (JToken student in data["Student"])
{
string id = student["Id"].ToString();
string name = student["Name"].ToString();
}
}

Transforming an array of name-value pairs in a JSON array with individual properties in the containing object

I'm trying to transform a JSON array to become object format.
Example:
{
"country": "USA",
"date": "2019-6-30",
"Speaker": [
{
"id": "name",
"value": "Tiger"
},
{
"id": "age",
"value": "35"
},
{
"id": "topic",
"value": ".NET"
}
]
}
I want to convert this to:
{
"country": "USA",
"date": "2019-6-30",
"name": "Tiger",
"age": 35,
"topic": ".NET"
}
I tried several ways, but had no luck. It seems I can't get the value of the internal array. Please help.
You can use Json.Net's LINQ-to-JSON API (JObjects) to transform your JSON:
JObject root = JObject.Parse(json);
JProperty arrayProp = root.Properties()
.Where(jp => jp.Value.Type == JTokenType.Array)
.FirstOrDefault();
if (arrayProp != null)
{
foreach (JObject item in arrayProp.Value.Children<JObject>())
{
root[(string)item["id"]] = item["value"];
}
arrayProp.Remove();
}
json = root.ToString();
This solution does not depend on the array property having any particular name, nor does it care what the item ids are. However, if there are any ids in the array which overlap with an existing property in the root object, the value in the array will replace the one already in the root object. Similarly, if there are any duplicate ids in the array, the last one will "win".
Working demo: https://dotnetfiddle.net/p3WkqN
You only need a couple of classes to deserialise this JSON, for example:
public class Data
{
public string Country { get; set; }
public string Date { get; set; }
// Deserialise the array as a list of 'SpeakerItem'
public List<SpeakerItem> Speaker { get; set; }
// These will throw exceptions if the id doesn't match, but it's a start
public string Name => Speaker.Single(s => s.Id == "name").Value;
public string Age => Speaker.Single(s => s.Id == "age").Value;
public string Topic => Speaker.Single(s => s.Id == "topic").Value;
}
public class SpeakerItem
{
public string Id { get; set; }
public string Value { get; set; }
}
Now you can do something like this:
var value = JsonConvert.DeserializeObject<Data>(json);
I have something like this using JSON.Net, first of all your json is wrong (you have dot in the end of country line). I have used DynamoObjects.
string json = #"
{
""country"": ""USA"",
""date"": ""2019-6-30"",
""Speaker"" : [
{
""id"": ""name"",
""value"": ""Tiger""
},
{
""id"": ""age"",
""value"": ""35""
},
{
""id"": ""topic"",
""value"": "".NET""
},
]
}";
dynamic animalJson = JsonConvert.DeserializeObject<dynamic>(json);
dynamic animal = new ExpandoObject();
animal.country = animalJson.country;
animal.date = animalJson.date;
animal.name = animalJson.Speaker[0].value;
animal.age = animalJson.Speaker[1].value;
animal.topic = animalJson.Speaker[2].value;
string modifiedAnimalJson = JsonConvert.SerializeObject(animal);

Verify Value exists in A Json Array with same key Names

I have this json body
[
{
"Id": 1,
"Name": "John",
"Icon": "Icon/someplace",
"SortOrder": 1
},
{
"Id": 2,
"Name": "Jessica",
"Icon": "Icon/someplace",
"SortOrder": 1
},
{
"Id": 3,
"Name": "Kevin",
"Icon": "Icon/someplace",
"SortOrder": 1
},
{
"Id": 4,
"Name": "Lint",
"Icon": "Icon/someplace",
"SortOrder": 1
},
{
...
}
]
I am adding Values to the json via API, I need to verify that the new value is present is the json body
I am trying to Covert the response to json,
public object Get_Json()
{
var response = GEt_Json_Body();
var json_string = JsonConvert.SerializeObject(response);
JArray UpdatedContent = JArray.Parse(json_string);
JObject Facility_Json = JObject.Parse(UpdatedContent[0].ToString());
Assert.NotNull(Facility_Json);
return Facility_Json;
}
This Only gives me back the first json:
{
"Id": 1,
"Name": "John",
"Icon": "Icon/someplace",
"SortOrder": 1
}
UpdatedContent[i] i allows me to get the other jsons in the array, the problem is I don't know where the json I Created using the API will be placed, how to get All of the JArray and verify that my entry is there?
Update:
This is my Call:
public List<FooModel> Get_Json_Body()
{
var request = new RestRequest();
request.Resource = string.Format("/api/get_something");
return Execute<FooMedl>(request, Endpoint);
}
public class FooModel
{
public int Id { get; set; }
public string Name { get; set; }
public string Icon { get; set; }
public int SortOrder { get; set; }
}
public List<T> Execute<T>(RestRequest request, string Endpoint) where T : new()
{
Client.BaseUrl = new Uri(Endpoint);
var response = Client.Execute<List<T>>(request);
Console.WriteLine(response.ResponseUri);
if (response.ErrorException != null)
{
string message = String.Format("Error retrieving response: \n{0} \n{1} \n{2} ",
response.Content, response.ErrorMessage, response.ErrorException);
Console.WriteLine(message);
var exception = new ApplicationException(message);
throw exception;
}
return Response.Data;
}
Update 2:
The Answer By Davig G helped me to solve the problem, Was able to verify my input via
if(data.Any(f => f.Name == "Kevin"))
{
Console.WriteLine("Kevin exists in the data");
}
I am returing a List of dictionaries From Get_Json() method Using DavigG's answer I am able to verify and access the specific keys and values within the list.
It's much easier to deal with concrete classes than pure JSON objects, I would recommend deserialising your JSON directly into a list of objects like this:
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
public string Icon { get; set; }
public int SortOrder { get; set; }
}
And the code to deserialise is:
var data = JsonConvert.DeserializeObject<List<Foo>>(response);
Now you can treat that object as you wish, for example:
if(data.Any(f => f.Name == "Kevin"))
{
Console.WriteLine("Kevin exists in the data");
}
Or even make modifications:
var kevin = data.SingleOrDefault(f => f.Name == "Kevin");
if(kevin != null)
{
kevin.SortOrder = 3;
}
And then if you need it to be back to the original JSON:
var json = JsonConvert.SerializeObject(data);

Parse JSON into C# object with "0" as object name inside JSON string

I receive a JSON obejct to start a web session, using MVC4. I need to parse it into a C# object for later use. I cannot change the object I receive - it comes from a third party.
The method I am using to parse:
MyClass jsonObject = new MyClass();
//The 2 lines below will not work with the given answer
//JavaScriptSerializer serializer = new JavaScriptSerializer();
//jsonObject = serializer.Deserialize<MyClass >(jsonString);
//Use this method instead
jsonObject = JsonConvert.DeserializeObject<MyClass>(jsonString);
QUESTION:
I'm having trouble defining the C# class for this part of the JSON due to the "0" name of internal object.
JSON:
{
"header": {
"to": {
"data": [],
"0": {
"data": [],
"value": "1101",
"domain": "DUNS"
}
},
"from": {
"data": [],
"0": {
"data": [],
"value": "1101",
"domain": "NetworkID"
}
},
"sender": {
"data": {
"UserAgent": "Test Client v1"
},
"0": {
"data": {
"SharedSecret": "test"
},
"value": "testing#smith.com",
"domain": "testClientID"
}
}
},
...
}
NOTE: I have tried online json to C# class generators, either failure, or creating a class with a name '0' which of course does not compile.
Comments:
MyClass would be something like this:
public class Header
{
public ToClass to = new ToClass ();
public FromClass to = new FromClass ();
}
public class ToClass
{
public Data[];
public zero 0 = new zero();
}
public class zero
{
public string data { get; set; }
public string value { get; set; }
public string domain { get; set; }
}
I believe you can use json.NET's property attribute system to correct this, the docs are here.
http://james.newtonking.com/json/help/index.html?topic=html/SerializationAttributes.htm
public class ToClass
{
public Data[];
[JsonProperty(PropertyName = "0")]
public zero Zero = new zero();
}
This will make it so the deserializer equates the json property named 0 with the C# property named Zero

How can I use ReadAsAsync<T> with this data schema?

I am using System.Net.Http.HttpClient, the version currently available in NuGet,
to retrieve data from a service in json format. The data roughly looks like this:
{
"schema": "Listing",
"data": {
"key": "28ba648c-de24-45d4-a7d9-70f810cf5438",
"children": [{
"kind": "type1",
"data": {
"body": "Four score and seven years ago...",
"parent_id": "2qh3l",
"report_count": 0,
"name": "c4j6yeh"
}
}, {
"kind": "type3",
"data": {
"domain": "abc.def.com",
"flagged": true,
"category": "news",
"saved": false,
"id": "t3dz0",
"created": 1335998011.0
}
}]
}
}
I use HttpContentExtensions.ReadAsAsync<T> to de-serialize that json string into an object graph. The type definitions looks roughly like this:
public class Response
{
public String schema { get;set; }
public ListingData data { get;set; }
}
public class ListingData
{
public string key { get;set; }
public List<OneItem> children { get;set; }
}
Here's the problem: I desire the type of the items in children to vary depending on the kind property. If kind is "type1" then I want to de-serialize an object of... let's call it Type1 . If kind is "type3" then I want an object of type Type3.
Right now, I can deserialize a List<Type1> or a List<Type3>, but I don't know how to tell the de-serialization logic to distinguish between the two.
I could merge all the properties of the "type1" data object and the "type3" data object into a single .NET Type. But the number of properties is large enough that this gets messy.
If the name of the property in the JSON (in this case data) were different, I could distinguish using that. If, for example, the data looked like this:
"children": [{
"kind": "type1",
"t1data": { ... }
}, {
"kind": "type3",
"t3data": { ... }
}]
...then I could do something like this in .NET:
public class OneItem
{
public string kind { get;set; }
public Type1 t1data { get;set; }
public Type3 t3data { get;set; }
}
But my data schema doesn't look like that.
Is it possible to choose the type for de-serialization by the content of the data? In other words,
look at the value of one property (in this case, kind) to determine how to de-serialize the content for another property (in this case, data).
Or is it possible to inject a filter or transformer that acts on the JSON before ReadAsAsync tries to deserialize it?
If so, How?
If you're ok w/ doing some pre-processing on your response and you can use Json.NET, you should be able to do what you want.
Given the following classes:
public class Response
{
public string schema
{
get;
set;
}
public ListingData data
{
get;
set;
}
}
public class ListingData
{
public string key
{
get;
set;
}
public List<object> children
{
get;
set;
}
}
public class Type1
{
public string body
{
get;
set;
}
public string parent_id
{
get;
set;
}
public int report_count
{
get;
set;
}
public string name
{
get;
set;
}
}
public class Type3
{
public string domain
{
get;
set;
}
public bool flagged
{
get;
set;
}
public string category
{
get;
set;
}
public bool saved
{
get;
set;
}
public string id
{
get;
set;
}
public double created
{
get;
set;
}
}
This test passes:
[Test]
public void RoundTrip()
{
var response = new Response
{
schema = "Listing",
data = new ListingData
{
key = "28ba648c-de24-45d4-a7d9-70f810cf5438",
children = new List<object>
{
new Type1
{
body = "Four score and seven years ago...",
parent_id = "2qh3l",
report_count = 0,
name = "c4j6yeh"
},
new Type3
{
domain = "abc.def.com",
flagged = true,
category = "news",
saved = false,
id = "t3dz0",
created = 1335998011.0
}
}
}
};
var jsonSerializerSettings = new JsonSerializerSettings
{
Formatting = Formatting.Indented,
TypeNameHandling = TypeNameHandling.Objects
};
string serializedResponse = JsonConvert.SerializeObject(response, jsonSerializerSettings);
Console.WriteLine(serializedResponse);
var roundTrippedResponse = JsonConvert.DeserializeObject<Response>(serializedResponse, jsonSerializerSettings);
Assert.That(roundTrippedResponse.data.children.First().GetType(), Is.EqualTo(typeof(Type1)));
Assert.That(roundTrippedResponse.data.children.Last().GetType(), Is.EqualTo(typeof(Type3)));
}
The output written to the console is:
{
"$type": "Test.Response, Test",
"schema": "Listing",
"data": {
"$type": "Test.ListingData, Test",
"key": "28ba648c-de24-45d4-a7d9-70f810cf5438",
"children": [
{
"$type": "Test.Type1, Test",
"body": "Four score and seven years ago...",
"parent_id": "2qh3l",
"report_count": 0,
"name": "c4j6yeh"
},
{
"$type": "Test.Type3, Test",
"domain": "abc.def.com",
"flagged": true,
"category": "news",
"saved": false,
"id": "t3dz0",
"created": 1335998011.0
}
]
}
}
So if you can transform your received response to match that of Json.NET's expected format, this will work.
To piece all of this together, you would need to write a custom MediaTypeFormatter and pass it to the ReadAsAsync<>() call.

Categories

Resources