I have a JSON API like this,
{
"pokemon": {
"currentPokemon": 1,
"total": 1,
"totalCount": 1,
},
"collections": [
{
"pokemonId": 2310,
"pokemonName": "Pikachu",
"pokemonType": "Land",
"status": {
"Active": "YES",
"Holder": "ASH"
},
"power": {
"Type": 10,
"name": "Thunder"
},
}
]
}
And I have the C# Classes for those API
Public ClassPokemonster
{
public class RootObject
{
[JsonProperty("pokemon")]
public Pokemon Pokemon { get; set; }
[JsonProperty("collections")]
public List<Collection> Collections { get; set; }
}
public class Pokemon
{
[JsonProperty("currentPokemon")]
public int CurrentPokemon { get; set; }
[JsonProperty("total")]
public int Total { get; set; }
[JsonProperty("totalCount")]
public int TotalCount { get; set; }
}
public class Collection
{
[JsonProperty("pokemonId")]
public int PokemonId { get; set; }
[JsonProperty("pokemonName")]
public string PokemonName { get; set; }
[JsonProperty("pokemonType")]
public string PokemonType { get; set; }
[JsonProperty("status")]
public Status Status { get; set; }
[JsonProperty("power")]
public Power Power { get; set; }
}
public class Status
{
[JsonProperty("Active")]
public string Active { get; set; }
[JsonProperty("Holder")]
public string Holder { get; set; }
}
public class Power
{
[JsonProperty("Type")]
public int Type { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
}
}
And I'm trying to assert those values matching the API values using this method
Driver.Instance.Navigate().GoToUrl(url);
//WebRequest
HttpWebRequest getRequest = (HttpWebRequest)WebRequest.Create(url);
getRequest.Method = "GET";
var getResponse = (HttpWebResponse)getRequest.GetResponse();
Stream newStream = getResponse.GetResponseStream();
StreamReader sr = new StreamReader(newStream);
//Deserialize JSON results
var result = sr.ReadToEnd();
Pokemonster deserializedObjects = JsonConvert.DeserializeObject<Pokemonster>(result);
I'm trying to assert in this way,
Assert.Equal("2310", deserializedObject.Collections.PokemonId.ToString());
My assert doesn't fetch the values inside the collections class such as pokemonoId pokemonNameand so on!
Help me getting through this!
The first issue (it's probably just an issue with how you've formatted it here, but I should mention it for completeness) is that you have:
Public ClassPokemonster
But the correct syntax is:
public class Pokemonster
Next, notice that all your other classes are declared inside the class Pokemonster. This kind of structure is called a nested type. The way you've designed it, the Pokemonster class itself contains no properties or methods, but the nested classes Pokemonster.RootObject, Pokemonster.Pokemon, etc. do have properties. So in order to correctly deserialize this type, you have to use:
Pokemonster.RootObject deserializedObjects =
JsonConvert.DeserializeObject<Pokemonster.RootObject>(result);
Finally, note that the property, Pokemonster.RootObject.Collections actually has the type List<Pokemonster.Collection>, but List<T> doesn't have any property named PokemonId (hence the error message). You'll have to access an item in this list to get any of it's properties, like this:
Assert.Equal("2310", deserializedObject.Collections[0].PokemonId.ToString());
Related
I'm trying to read a list of objects from a public WEB Api that provides a JSON file with an array of objects, I'm using Blazor and the Net 5 platform.
The de-serialization fails with this error:
System.Text.Json.JsonException: The JSON value could not be converted to Meme[].
I suspect I'm modeling the "receiving" object incorrectly, should I change my code or use other libraries for this code to succeed?
The Api can be found at this endpoint, I tried reading the response in these two ways:
var response = await Http.GetFromJsonAsync<Meme[]>("https://api.imgflip.com/get_memes");
and
var httpResponse = await Http.GetAsync("https://api.imgflip.com/get_memes");
var response = await httpResponse.Content.ReadFromJsonAsync<Meme[]>();
the Meme class is declared as such:
public string Id { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public int BoxCount { get; set; }
and the response should contain this content:
"success": true,
"data": {
"memes": [
{
"id": "181913649",
"name": "Drake Hotline Bling",
"url": "https://i.imgflip.com/30b1gx.jpg",
"width": 1200,
"height": 1200,
"box_count": 2
},
{
...
},
... ]
}
These are the libraries I'm including:
using System.Net.Http;
using System.Net.Http.Json;
The response includes more than your Memes itself. The Meme array is within the object data and memes. Modell the entire response and you will be able to deserialize it. So you will need the following:
public class Response
{
public bool success { get; set; }
public Data data { get; set; }
}
public class Data
{
public Meme[] memes { get; set; }
}
public class Meme
{
public string id { get; set; }
public string name { get; set; }
public string url { get; set; }
public int width { get; set; }
public int height { get; set; }
public int box_count { get; set; }
}
// Now you can use that like this:
var response = await httpResponse.Content.ReadFromJsonAsync<Response>();
Note that there is a handy tool in VS that generated that for me. You can paste JSON as classes under Edit > Paste Special > Paste JSON as Classes. You can still use "normal" camel casing but you may have to instruct the serializer to do not match property names case-sensitive.
I am using JObject to read various api responses. With the following json
{"API":{"Year":["2020","2019","2018","2017","2016","2015"],
"Status": {"Message":"The call returned successfully with years"}}}
I can use this:
dynamic json= JObject.Parse(s);
string[] yrs = json.API.Year.ToObject<string[]>();
where s is the json object.
This works perfectly to give me a simple array of years.
I am having difficulty parsing multi dimensions in the json response. when i have the following:
{"API":{"Category":[
{"GroupName":"Exterior Accessories","GroupID":"2",
"Items":
[{"Id":"64","Value":"Body Part"},
{"Id":"20","Value":"Body Styling"},
{"Id":"7","Value":"Bras and Hood Protectors"}]
},
{"GroupName":"Interior Accessories","GroupID":"4",
"Items":
[{"Id":"21","Value":"Carpet",
{"Id":"2","Value":"Doors and Components"},
{"Id":"8","Value":"Floor Protection"}]
},
],
"Status": {"Message":"The call (api.v12.estore.catalograck.com) returned successfully with categories.","DataFound":true,"TimeStamp":"02/02/2020 11:48:27","InternalError":false}}}
How can I parse this into a multi-dimensional array?
Since Years you used was a simple built in element (string), you dont need to create any classes.. but the Categories you have in your JSON is a object. To access the Categories in a way you can access its elements, I would recommend creating the necessary classes and then using the Category list of the root object to do what you need.
Example code
public class Item
{
public string Id { get; set; }
public string Value { get; set; }
}
public class Category
{
public string GroupName { get; set; }
public string GroupID { get; set; }
public List<Item> Items { get; set; }
}
public class Status
{
public string Message { get; set; }
public bool DataFound { get; set; }
public string TimeStamp { get; set; }
public bool InternalError { get; set; }
}
public class API
{
public List<Category> Category { get; set; }
public Status Status { get; set; }
}
public class RootObject
{
public API API { get; set; }
}
Above are the needed classes for succesful deserialization where RootObject class is the parent.
Use the following in your Main method.,
RootObject root = JsonConvert.DeserializeObject<RootObject>(json);
List<Category> categories = root.API.Category;
This is json:
{
"odata.metadata": ".....",
"value": [
{
"AbsEntry": 10,
"ItemNo": "....",
"UoMEntry": -1,
"Barcode": "2000000000022",
"FreeText": "Ean13"
}
]
}
This is class:
public class BarCode
{
public int AbsEntry { get; set; }
public string ItemNo { get; set; }
public string Barcode { get; set; }
public string FreeText { get; set; }
}
This method return null:
BarCode barcode = JsonParser.DeserializeObjcet<BarCode>(json);
Are there any properties or other that can cause the call DeserializeObject to deserialize me only the fields of my classes (the names are exactly those of the Json)?
You need to create class like below not BarCode
public class Value
{
public int AbsEntry { get; set; }
public string ItemNo { get; set; }
public int UoMEntry { get; set; }
public string Barcode { get; set; }
public string FreeText { get; set; }
}
or you can change the JSON format
"BarCode": [
{
"AbsEntry": 10,
"ItemNo": "....",
"UoMEntry": -1,
"Barcode": "2000000000022",
"FreeText": "Ean13"
}
]
The structure of your class should match the structure of your JSON if you want the deserialization to succeed.
If you want a partial deserialization e.g. only deserializing the values property into your class you can use this code:
JObject jObject = JObject.Parse(json);
BarCode barcode = jObject["values"].Children().First().ToObject<BarCode>();
With this solution you don't need to refactor your class or adding a new one.
You are missing the root object :
public class RootObject
{
public string __invalid_name__odata.metadata { get; set; }
public List<BarCode> value { get; set; }
}
note that the invalid name oprperty can just be remove so it will be igrored!
Even if you want only the Value part you will have to deserialise from the root, then navigate to the child properties you need.
i have a web service that returns a Json string.
my problem is that i have difficulties to read it
i tried with:
JavaScriptSerializer jsSerializer = new JavaScriptSerializer();
string jsonData = reader.ReadToEnd();
var myobj = jsSerializer.Deserialize<List<CinfoRichiesta>>(jsonData);
but how can i get values from "students" and "locations"?
with javascript i used :" var j = jQuery.parseJSON(msg.d);" but i think with c# code would be different
this is an example of string:
{"Questions":{
"id":"2",
"BOOK":"3",
"students":{
"class":"3",
"theme","43"
},
"locations":{
"h":"0",
"L":"3"
}
}
}
First off, your JSON isn't valid so thats the first problem you have. You can verify this at http://jsonlint.com/ for example.
i have currently fixed this in the following way:
{
"Questions": {
"id": "2",
"BOOK": "3",
"students": {
"class": "3",
"theme": "na",
"43": "na"
},
"locations": {
"h": "0",
"L": "3"
}
}
}
Second your class should be correct, with the current JSON this should look something like this
public class Rootobject
{
public Questions Questions { get; set; }
}
public class Questions
{
public string id { get; set; }
public string BOOK { get; set; }
public Students students { get; set; }
public Locations locations { get; set; }
}
public class Students
{
public string _class { get; set; }
public string theme { get; set; }
public string _43 { get; set; }
}
public class Locations
{
public string h { get; set; }
public string L { get; set; }
}
After this you can deserialize it like this
var myobj = jsSerializer.Deserialize<List<Rootobject>>(jsonData);
And then you can get the information like this
myobj.Questions.students._class
You're deserializing to a collection of type CinfoRichiesta, which should hold a property value for students and locations.
Assuming that your JSON is correctly formatted and your class definition is suitable for the response (I recommend double checking it by pasting the entire response string into json2csharp.com)
Once that's all validated, you should be able to see the students and locations collections internally like so:
foreach(Question q in myobj)
{
Console.WriteLine(q.students.class)
}
which should give you the result of 3.
edit
I think your main question is why you're unable to access the properties of students and locations. Make sure Students is its own class as such:
public class Students
{
public int class { get; set; }
public int theme { get; set; }
}
and your locations class should be:
public class Locations
{
public int h { get; set; }
public int l { get; set; }
}
You should then have a questions class that instaniates both students and locations, as such:
public class Questions
{
public int id { get; set; }
public int book { get; set; }
public Student students { get; set; }
public Locations locations { get; set; }
}
When working with JSON deserialization, it's important that your object property names (ie class) match the response string in terms of case. So If you wrote it as public int Theme, it won't directly map.
Slightly annoying in terms of coding standards, but hey ho :-)
I'd like to consume a REST Api and deserialize the nested JSON Response. For that purpose I tried to create some POCO classes which represent the JSON Response [1].
The response looks like this:
{
"success": true,
"message": "OK",
"types":
[
{
"name": "A5EF3-ASR",
"title": "ITIL Foundation Plus Cloud Introduction",
"classroomDeliveryMethod": "Self-paced Virtual Class",
"descriptions": {
"EN": {
"description": "some Text null",
"overview": null,
"abstract": "Some other text",
"prerequisits": null,
"objective": null,
"topic": null
}
},
"lastModified": "2014-10-08T08:37:43Z",
"created": "2014-04-28T11:23:12Z"
},
{
"name": "A4DT3-ASR",
"title": "ITIL Foundation eLearning Course + Exam",
"classroomDeliveryMethod": "Self-paced Virtual Class",
"descriptions": {
"EN": {
"description": "some Text"
(...)
So I created the following POCO classes:
public class Course
{
public bool success { get; set; }
public string Message { get; set; }
public List<CourseTypeContainer> Type { get; set; }
}
/* each Course has n CourseTypes */
public class CourseType
{
public string Name { get; set; }
public string Title { get; set; }
public List<CourseTypeDescriptionContainer> Descriptions { get; set; }
public DateTime LastModified { get; set; }
public DateTime Created { get; set; }
}
public class CourseTypeContainer
{
public CourseType CourseType { get; set; }
}
/* each CourseType has n CourseTypeDescriptions */
public class CourseTypeDescription
{
public string Description { get; set; }
public string Overview { get; set; }
public string Abstract { get; set; }
public string Prerequisits { get; set; }
public string Objective { get; set; }
public string Topic { get; set; }
}
public class CourseTypeDescriptionContainer
{
public CourseTypeDescription CourseTypeDescription { get; set; }
}
And this is the API Code:
var client = new RestClient("https://www.someurl.com");
client.Authenticator = new HttpBasicAuthenticator("user", "password");
var request = new RestRequest();
request.Resource = "api/v1.0/types";
request.Method = Method.GET;
request.RequestFormat = DataFormat.Json;
var response = client.Execute<Course>(request);
EDIT 1: I found a Typo, the Type property in AvnetCourse should be named Types:
public List<AvnetCourseTypeContainer> Type { get; set; } // wrong
public List<AvnetCourseTypeContainer> Types { get; set; } // correct
Now the return values look like:
response.Data.success = true // CORRECT
repsonse.Data.Message = "OK" // CORRECT
response.Data.Types = (Count: 1234); // CORRECT
response.Data.Types[0].AvnetCourseType = null; // NOT CORRECT
EDIT 2: I implemented the Course.Types Property using a List<CourseType> instead of a List<CourseTypeContainer>, as proposed by Jaanus. The same goes for the CourseTypeDescriptionContainer:
public List<CourseTypeContainer> Type { get; set; } // OLD
public List<CourseTypeDescriptionContainer> Descriptions { get; set; } // OLD
public List<CourseType> Type { get; set; } // NEW
public List<CourseTypeDescription> Descriptions { get; set; } // NEW
Now the response.Data.Types finally are properly filled. However, the response.Data.Types.Descriptions are still not properly filled, since there is an additional language layer (e.g. "EN"). How can I solve this, without creating a PACO for each language?
EDIT 3: I had to add an additional CourseTypeDescriptionDetails class, where I would store the descriptive Data. In my CourseTypeDescription I added a property of the Type List for each language. Code Snippet:
public class AvnetCourseType
{
public List<CourseTypeDescription> Descriptions { get; set; }
// other properties
}
public class CourseTypeDescription
{
public List<CourseTypeDescriptionDetails> EN { get; set; } // English
public List<CourseTypeDescriptionDetails> NL { get; set; } // Dutch
}
public class CourseTypeDescriptionDetails
{
public string Description { get; set; }
public string Overview { get; set; }
public string Abstract { get; set; }
public string Prerequisits { get; set; }
public string Objective { get; set; }
public string Topic { get; set; }
}
It works now, but I need to add another property to CourseTypeDescription for each language.
OLD: The return values are
response.Data.success = true // CORRECT
repsonse.Data.Message = "OK" // CORRECT
response.Data.Type = null; // WHY?
So why does my response.Type equal null? What am I doing wrong?
Thank you
Resources:
[1] RestSharp Deserialization with JSON Array
Try using this as POCO:
public class Course
{
public bool success { get; set; }
public string message { get; set; }
public List<CourseTypeContainer> Types { get; set; }
}
Now you have list of CourseTypeContainer.
And CourseTypeContainer is
public class CourseTypeContainer
{
public CourseType CourseType { get; set; }
}
So when you are trying to get response.Data.Types[0].AvnetCourseType , then you need to have field AvnetCourseType inside CourseTypeContainer
Or I think what you want is actually this public List<CourseType> Types { get; set; }, you don't need a container there.
Just in case this helps someone else, I tried everything here and it still didn't work on the current version of RestSharp (106.6.2). RestSharp was completely ignoring the RootElement property as far as I could tell, even though it was at the top level. My workaround was to manually tell it to pull the nested JSON and then convert that. I used JSON.Net to accomplish this.
var response = restClient.Execute<T>(restRequest);
response.Content = JObject.Parse(response.Content)[restRequest.RootElement].ToString();
return new JsonDeserializer().Deserialize<T>(response);
I used http://json2csharp.com/ to create C# classes from JSON.
Then, renamed RootObject to the ClassName of the model file I'm creating
All the data in the nested json was accessible after RestSharp Deserializitaion similar to responseBody.data.Subject.Alias
where data, Subject and Alias are nested nodes inside the response JSON received.