I am trying trying to Deserialize the Devices json structure below with c#, newtonsoft, and .net 3.5, put have been unable to produce code that works as the "qxdddh" and "q0tL6au" are dynamically generated names.
Ultimately creating a list or array of thermostat classes for each thermostat in the structure (in this case "qxdddh" and "q0tL6au").
{
"thermostats": {
"qxdddh": {
"locale": "en-US",
"temperature_scale": "F",
"is_using_emergency_heat": false,
"has_fan": true,
"software_version": "4.1",
"has_leaf": true,
"device_id": "qxdddh",
"name": "",
"can_heat": true,
"can_cool": true,
"hvac_mode": "heat",
"target_temperature_c": 12.5,
"target_temperature_f": 55,
"target_temperature_high_c": 24.0,
"target_temperature_high_f": 75,
"target_temperature_low_c": 20.0,
"target_temperature_low_f": 68,
"ambient_temperature_c": 21.0,
"ambient_temperature_f": 70,
"away_temperature_high_c": 24.0,
"away_temperature_high_f": 76,
"away_temperature_low_c": 12.5,
"away_temperature_low_f": 55,
"structure_id": "ryWu-tRQstxux0tYhmZ8ESsrGgDjDQ",
"fan_timer_active": false,
"name_long": "Thermostat",
"is_online": true
},
"q0tL6au": {
"locale": "en-US",
"temperature_scale": "F",
"is_using_emergency_heat": false,
"has_fan": true,
"software_version": "4.1",
"has_leaf": true,
"device_id": "q0tL6au",
"name": "Den",
"can_heat": false,
"can_cool": true,
"hvac_mode": "off",
"target_temperature_c": 20.5,
"target_temperature_f": 69,
"target_temperature_high_c": 24.0,
"target_temperature_high_f": 75,
"target_temperature_low_c": 20.0,
"target_temperature_low_f": 68,
"ambient_temperature_c": 23.0,
"ambient_temperature_f": 73,
"away_temperature_high_c": 24.0,
"away_temperature_high_f": 76,
"away_temperature_low_c": 12.5,
"away_temperature_low_f": 55,
"structure_id": "ryWu-tqNu0tYhmZ8ESsrGgDjDQ",
"fan_timer_active": false,
"name_long": "Den Thermostat",
"is_online": true
}
}
}
The initial attempt of code was
public class Devices
{
public TstatDetails[] thermostats { get; set; }
}
public class TstatDetails
{
public string locale { get; set; }
public string temperature_scale { get; set; }
public string is_using_emergency_heat { get; set; }
public string has_fan { get; set; }
public string software_version { get; set; }
public string has_leaf { get; set; }
public string device_id { get; set; }
public string name { get; set; }
public string can_heat { get; set; }
public string can_cool { get; set; }
public string hvac_mode { get; set; }
public string target_temperature_c { get; set; }
public string target_temperature_f { get; set; }
public string target_temperature_high_c { get; set; }
public string target_temperature_high_f { get; set; }
public string target_temperature_low_c { get; set; }
public string target_temperature_low_f { get; set; }
public string ambient_temperature_c { get; set; }
public string ambient_temperature_f { get; set; }
public string away_temperature_high_c { get; set; }
public string away_temperature_high_f { get; set; }
public string away_temperature_low_c { get; set; }
public string away_temperature_low_f { get; set; }
public string structure_id { get; set; }
public string fan_timer_active { get; set; }
public string name_long { get; set; }
public string is_online { get; set; }
}
and
Devices tstats = (Devices) Newtonsoft.Json.JsonConvert.DeserializeObject<Devices>(jsonstring);
Which produces and exception with the following description
Cannot deserialize the current JSON object (e.g. {"name":"value"})
into type 'NestTest.NestOAuth2+TstatDetails[]' because the type
requires a JSON array (e.g. [1,2,3]) to deserialize correctly.
To 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 from a JSON object.
I understand the error (i think) but being fairly new to c# am unsure on how to correct for this scenario.
Just change your Devices class to use a Dictionary<,> instead of an array:
public class Devices
{
public Dictionary<string, TstatDetails> thermostats { get; set; }
}
JSON.NET will interpret each property of the thermostats object in the JSON as an entry in the dictionary, and populate it appropriately. (Your calling code remains exactly the same.)
Then you'll have all the thermostats available by ID. For example:
TstatDetails details = tstats["qxdddh"];
Once that's working, I'd strongly recommend you try to make all the property names more conventional :)
Your Json object should be an array, not just an object.
It should basically be wrapped with [ .... ] (of which within you can definitely have multiple objects) rather than { ... }.
It should probably look similar to this:
[
{
"locale": "en-US",
"temperature_scale": "F",
"is_using_emergency_heat": false,
...
},
{
"locale": "en-US",
"temperature_scale": "F",
"is_using_emergency_heat": false,
...
}
]
EDIT
OK, I can't compete with The Skeet.
But I'll leave my answer because it's correct as well (just one option to attack the problem).
Related
I am loading JSON data in my Unity project via Newtonsoft JsonConvert.Deserialize. This works in Editor and the json is valid. However, as soon as I make a Windows build, the serialized data is empty / filled with default values instead.
This is also shown when I serialize the deserialized data and output it:
Json string read:
{
"galleries": [{
"id": "Acker",
"title": "Auf dem Acker",
"slides": [],
"path": "/ChangeableContentData/Auf dem Acker",
"showAllInFolder": true,
"foldersRecursive": false,
"enableSlideshow": false,
"slideshowSlideDuration": 5.0
}],
"cardMenus": [],
"urls": []
}
reserialized:
{
"galleries": [{
"id": null,
"title": "",
"slides": null,
"path": "",
"showAllInFolder": true,
"foldersRecursive": true,
"enableSlideshow": false,
"slideshowSlideDuration": 5.0
}],
"cardMenus": []
}
Here is the relevant C# class:
[System.Serializable]
[Preserve]
public partial class UIContentData
{
[JsonProperty("galleries")]
public List<UIContentGalleryData> galleries { get; set; }
[JsonProperty("cardMenus")]
public List<UIContentCardMenuData> cardMenus { get; set; }
[JsonProperty("urls")]
public List<UIContentUrlData> urls { get; set; }
public UIContentData() { }
}
[System.Serializable]
[Preserve]
public partial class UIContentGalleryData
{
public string id { get; set; }
[JsonProperty("title")]
public string title { get; set; } = ""; // optional
[JsonProperty("slides")]
public List<UIContentGallerySlide> slides { get; set; }
[JsonProperty("path")]
public string baseFolderPath { get; set; } = "";
[JsonProperty("showAllInFolder")]
public bool showAllInFolder { get; set; } = true; // optional
[JsonProperty("foldersRecursive")]
public bool foldersRecursive { get; set; } = true; // optional
[JsonProperty("enableSlideshow")]
public bool enableSlideshow { get; set; } = false; // optional
[JsonProperty("slideshowSlideDuration")]
public float slideshowSlideDuration { get; set; } = 5f; // optional
public UIContentGalleryData() { }
}
Edit for clarity: I am using UIContentData object to deserialize.
JsonConvert.DeserializeObject<UIContentData>(json);
Any help is much appreciated!
You are using the wrong class,should create one more. This code was tested and working properly
var data=JsonConvert.DeserializeObject<Root>(json);
public class Root
{
public List<UIContentGalleryData> galleries { get; set; }
public List<object> cardMenus { get; set; }
public List<object> urls { get; set; }
}
and remove slides property from UIContentGallerySlide
[JsonProperty("slides")]
public List<UIContentGallerySlide> slides { get; set; }
Found the issue. Unity was stripping the class definitions I believe. Adding a link.xml with preserve="all" for the assembly containing them solved the problem!
I am using this code to read a json file firstSession.json and display it on a label.
var assembly = typeof(ScenarioPage).GetTypeInfo().Assembly;
string jsonFileName = "firstSession.json";
Stream stream = assembly.GetManifestResourceStream($"{assembly.GetName().Name}.{jsonFileName}");
using (var reader = new StreamReader(stream))
{
var json = reader.ReadToEnd(); //json string
var data = JsonConvert.DeserializeObject<SessionModel>(json);
foreach (SessionModel scenario in data)
{
scenarioName.Text = scenario.title;
break;
}
scenarioName.Text = data.title; // scenarioName is the name of the label
}
SessionModel.cs looks like:
public class SessionModel : IEnumerable
{
public int block { get; set; }
public string name { get; set; }
public string title { get; set; }
public int numberMissing { get; set; }
public string word1 { get; set; }
public string word2 { get; set; }
public string statement1 { get; set; }
public string statement2 { get; set; }
public string question { get; set; }
public string positive { get; set; } // positive answer (yes or no)
public string negative { get; set; } // negative answer (yes or no)
public string answer { get; set; } // positive or negative
public string type { get; set; }
public string format { get; set; }
public string immersion { get; set; }
public IEnumerator GetEnumerator()
{
throw new NotImplementedException();
}
}
The beginning of my json is:
{
"firstSession": [
{
"block": 1,
"name": "mark",
"title": "mark's house",
"numberMissing": 1,
"word1": "distracted",
"word2": "None",
"statement1": "string 1",
"statement2": "None",
"question": "question",
"positive": "No",
"negative": "Yes",
"answer": "Positive",
"type": "Social",
"format": "Visual",
"immersion": "picture"
},
I am getting a Newtonsoft.Json.JsonSerializationException: Cannot deserialize the current JSON object into type "MyProject.SessionModel" because the type requires a JSON array to deserialize correctly. To fix this error either change the JSON to a JSON array or change the deserialized type so that it is a normal .NET type that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object. Path 'firstSession', line 2, position 17.
How can I convert the json string to a json array? Or make one of the other modifications the debugger suggests?
you need to create a wrapper class (json2csharp.com will help you do this)
public class Root {
public List<SessionModel> firstSession { get; set; }
}
then
var data = JsonConvert.DeserializeObject<Root>(json);
data.firstSession will be a List<SessionModel>
Create a new Class and have firstSession as List of SessionModel.
public class Sessions
{
public List<SessionModel> firstSession { get; set; }
}
Remove IEnumerable from the SessionModel
public class SessionModel
{
public int block { get; set; }
public string name { get; set; }
public string title { get; set; }
}
Change thedeserialization part as follows
var data = JsonConvert.DeserializeObject(line);
foreach (SessionModel scenario in data.firstSession)
{
//Here you can get each sessionModel object
Console.WriteLine(scenario.answer);
}
I have below JSON file,
[
{
"applicationConfig": {
"Name": "Name1",
"Site": "Site1"
},
"pathConfig": {
"SourcePath": "C:\\Temp\\Outgoing1",
"TargetPath": "C:\\Files"
},
"credentialConfig": {
"Username": "test1",
"password": "super1"
}
},
{
"applicationConfig": {
"Name": "Name2",
"Site": "Site2"
},
"pathConfig": {
"SourcePath": "C:\\Temp\\Outgoing2",
"TargetPath": "C:\\Files"
},
"credentialConfig": {
"Username": "test2",
"password": "super2"
}
}
]
And below are C# classes structure,
public class Configurations
{
public List<ApplicationConfig> ApplicationConfigs { get; set; }
public List<PathConfig> PathConfigs { get; set; }
public List<CredentialConfig> CredentialConfigs { get; set; }
}
public class ApplicationConfig
{
public string Name { get; set; }
public string Site { get; set; }
}
public class PathConfig
{
public string SourcePath { get; set; }
public string TargetPath { get; set; }
}
public class CredentialConfig
{
public string Username { get; set; }
public string password { get; set; }
}
Now trying to load JSON and getting below error,
using (var streamReader = new StreamReader(#"./Config.json"))
{
var X = JsonConvert.DeserializeObject<Configurations>(streamReader.ReadToEnd());
}
$exception {"Cannot deserialize the current JSON array (e.g. [1,2,3])
into type 'ConsoleApp8.Configurations' because the type requires a
JSON object (e.g. {\"name\":\"value\"}) to deserialize
correctly.\r\nTo fix this error either change the JSON to a JSON
object (e.g. {\"name\":\"value\"}) or change the deserialized type to
an array or a type that implements a collection interface (e.g.
ICollection, IList) like List that can be deserialized from a JSON
array. JsonArrayAttribute can also be added to the type to force it to
deserialize from a JSON array.\r\nPath '', line 1, position
1."} Newtonsoft.Json.JsonSerializationException
What else I need to serialize?
Your JSON represents an array - although the closing [ should be a ]. But you're trying to serialize it into a single Configurations object. Additionally, you seem to be expecting separate arrays for the application configs, path configs and credential configs - whereas your JSON shows an array of objects, each of which has all three.
I suspect you want:
public class Configuration
{
[JsonProperty("applicationConfig")]
ApplicationConfig ApplicationConfig { get; set; }
[JsonProperty("pathConfig")]
PathConfig PathConfig { get; set; }
[JsonProperty("credentialConfig")]
CredentialConfig CredentialConfig { get; set; }
}
// Other classes as before, although preferably with the password property more conventionally named
Then use:
List<Configuration> configurations =
JsonConvert.DeserializeObject<List<Configuration>>(streamReader.ReadToEnd());
You'll then have a list of configuration objects, each of which will have the three "subconfiguration" parts.
Your JSON class definition is close but not quite. Moroever the last [ must be ]
JSON class definition is created wtih QuickType
public partial class Configuration
{
[JsonProperty("applicationConfig")]
public ApplicationConfig ApplicationConfig { get; set; }
[JsonProperty("pathConfig")]
public PathConfig PathConfig { get; set; }
[JsonProperty("credentialConfig")]
public CredentialConfig CredentialConfig { get; set; }
}
public partial class ApplicationConfig
{
[JsonProperty("Name")]
public string Name { get; set; }
[JsonProperty("Site")]
public string Site { get; set; }
}
public partial class CredentialConfig
{
[JsonProperty("Username")]
public string Username { get; set; }
[JsonProperty("password")]
public string Password { get; set; }
}
public partial class PathConfig
{
[JsonProperty("SourcePath")]
public string SourcePath { get; set; }
[JsonProperty("TargetPath")]
public string TargetPath { get; set; }
}
Finally you need to serialize with
var config_list = JsonConvert.DeserializeObject<List<Configuration>>(streamReader.ReadToEnd());
I think it is a typo, you are opening the square bracket instead of closing it in the JSON file.
[ {
"applicationConfig": {
"Name": "Name1",
"Site": "Site1"
},
"pathConfig": {
"SourcePath": "C:\Temp\Outgoing1",
"TargetPath": "C:\Files"
},
"credentialConfig": {
"Username": "test1",
"password": "super1"
} }, {
"applicationConfig": {
"Name": "Name2",
"Site": "Site2"
},
"pathConfig": {
"SourcePath": "C:\Temp\Outgoing2",
"TargetPath": "C:\Files"
},
"credentialConfig": {
"Username": "test2",
"password": "super2"
} } [ <-HERE
Edit
I notice this is getting negative votes, I imagine it's for not showing research effort.
I've looked at a lot of stack overflow posts. I can't find an example with JSON this heavily nested that has been accessed with examples of how the data stored in the JSON has been manipulated afterwards.
Problem and Aim
Using restsharp I've received a JSON response to an API call I made however I'm struggling to deserialize the response I'm getting. The data I want to use seems to be a nested array (below).
My aim is to then pass the contents of that array to a variable, check if it's populated, take the first item of that array, and then set the properties in that item equal to objects in my database.
This is the error message I'm getting with my current code. It seems like I'm treating the Hit object as the wrong type of object however after a few hours of banging my head against a wall I'm not entirely sure why in particular this isn't working for this particular JSON structure.
Current Code
var hitresult = JsonConvert.DeserializeObject( response.Content, typeof( List<Hit> ) ) as List<Hit>;
if (hitresult.Any())
{
var address = hitresult.FirstOrDefault();
verified = true;
result = string.Format( "UDPRN: {0}", address.udprn );
location.Street1 = address.line_1;
location.Street2 = address.line_2;
location.City = address.post_town;
location.State = address.county;
location.PostalCode = address.postcode;
Error Message
An exception of type 'Newtonsoft.Json.JsonSerializationException'
occurred in Newtonsoft.Json.dll but was not handled in user code
Additional information: Cannot deserialize the current JSON object
(e.g. {"name":"value"}) into type
'System.Collections.Generic.List`1[org.hopecorby.LocationService.Hit]'
because the type requires a JSON array (e.g. [1,2,3]) to deserialize
correctly.
To 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 from a JSON object.
Path 'result', line 1, position 10.
Example JSON
{
"result": {
"total": 2,
"limit": 10,
"page": 0,
"hits": [
{
"dependant_locality": "",
"postcode_type": "L",
"po_box": "",
"post_town": "LONDON",
"delivery_point_suffix": "1A",
"double_dependant_locality": "",
"su_organisation_indicator": " ",
"longitude": -0.127695242183412,
"department_name": "",
"district": "Westminster",
"building_name": "",
"dependant_thoroughfare": "",
"northings": 179951,
"premise": "10",
"postcode_outward": "SW1A",
"postcode_inward": "2AA",
"sub_building_name": "",
"eastings": 530047,
"postcode": "SW1A 2AA",
"country": "England",
"udprn": 23747771,
"line_3": "",
"organisation_name": "Prime Minister & First Lord Of The Treasury",
"ward": "St James's",
"county": "",
"line_1": "Prime Minister & First Lord Of The Treasury",
"building_number": "10",
"thoroughfare": "Downing Street",
"line_2": "10 Downing Street",
"latitude": 51.5035398826274
},
{
"dependant_locality": "",
"postcode_type": "S",
"po_box": "",
"post_town": "LONDON",
"delivery_point_suffix": "1B",
"double_dependant_locality": "",
"su_organisation_indicator": " ",
"longitude": -0.122624730080001,
"department_name": "",
"district": "Camden",
"building_name": "Downing Court",
"dependant_thoroughfare": "",
"northings": 182178,
"premise": "Flat 10, Downing Court",
"postcode_outward": "WC1N",
"postcode_inward": "1LX",
"sub_building_name": "Flat 10",
"eastings": 530342,
"postcode": "WC1N 1LX",
"country": "England",
"udprn": 26245117,
"line_3": "Grenville Street",
"organisation_name": "",
"ward": "Bloomsbury",
"county": "",
"line_1": "Flat 10",
"building_number": " ",
"thoroughfare": "Grenville Street",
"line_2": "Downing Court",
"latitude": 51.5234851731108
}
]
},
"code": 2000,
"message": "Success"
}
My Models(Created with json2charp)
public class Hit
{
public string dependant_locality { get; set; }
public string postcode_type { get; set; }
public string po_box { get; set; }
public string post_town { get; set; }
public string delivery_point_suffix { get; set; }
public string double_dependant_locality { get; set; }
public string su_organisation_indicator { get; set; }
public double longitude { get; set; }
public string department_name { get; set; }
public string district { get; set; }
public string building_name { get; set; }
public string dependant_thoroughfare { get; set; }
public int northings { get; set; }
public string premise { get; set; }
public string postcode_outward { get; set; }
public string postcode_inward { get; set; }
public string sub_building_name { get; set; }
public int eastings { get; set; }
public string postcode { get; set; }
public string country { get; set; }
public int udprn { get; set; }
public string line_3 { get; set; }
public string organisation_name { get; set; }
public string ward { get; set; }
public string county { get; set; }
public string line_1 { get; set; }
public string building_number { get; set; }
public string thoroughfare { get; set; }
public string line_2 { get; set; }
public double latitude { get; set; }
}
public class Result
{
public int total { get; set; }
public int limit { get; set; }
public int page { get; set; }
public List<Hit> hits { get; set; }
}
public class RootObject
{
public Result result { get; set; }
public int code { get; set; }
public string message { get; set; }
}
The Deserializer expects a JSON Array. Your JSON is a JSON Object containing a JSON Array. There's no way the deserializer can know that you expect it to start its work with the hits array.
You need to deserialize as the RootObject. Then you would be able to refer to the List<Hit> as a property of the Result.
Update:
The following code should give you a sense of what I mean. I tested this and it works for me, with your objects and your JSON.
var sr = new StreamReader(#"C:\Users\danielc\Documents\Visual Studio 2012\Projects\TestJSON\TestJSON\response.json");
string json = sr.ReadToEnd();
sr.Close();
var root = JsonConvert.DeserializeObject<RootObject>(json);
var result = root.result;
var hits = result.hits;
if (hits.Any())
{
var address = hits.FirstOrDefault();
var udprn = string.Format("UDPRN: {0}", address.udprn);
Console.WriteLine(udprn);
}
Console.Read();
My JSON feed has nested objects like this:
{
"id": 1765116,
"name": "StrozeR",
"birth": "2009-08-12",
"avatar": "http:\/\/static.erepublik.com\/uploads\/avatars\/Citizens\/2009\/08\/12\/f19db99e9baddad73981d214a6e576ef_100x100.jpg",
"online": true,
"alive": true,
"ban": null,
"level": 61,
"experience": 183920,
"strength": 25779.42,
"rank": {
"points": 133687587,
"level": 63,
"image": "http:\/\/www.erepublik.com\/images\/modules\/ranks\/god_of_war_1.png",
"name": "God of War*"
},
"elite_citizen": false,
"national_rank": 6,
"residence": {
"country": {
"id": 81,
"name": "Republic of China (Taiwan)",
"code": "TW"
},
"region": {
"id": 484,
"name": "Hokkaido"
}
}
}
and my object classes are like this:
class Citizen
{
public class Rank
{
public int points { get; set; }
public int level { get; set; }
public string image { get; set; }
public string name { get; set; }
}
public class RootObject
{
public int id { get; set; }
public string name { get; set; }
public string avatar { get; set; }
public bool online { get; set; }
public bool alive { get; set; }
public string ban { get; set; }
public string birth { get; set; }
public int level { get; set; }
public int experience { get; set; }
public double strength { get; set; }
public List<Rank> rank { get; set; }
}
}
I try to parse my JSON data with following code
private async void getJSON()
{
var http = new HttpClient();
http.MaxResponseContentBufferSize = Int32.MaxValue;
var response = await http.GetStringAsync(uri);
var rootObject = JsonConvert.DeserializeObject<Citizen.RootObject>(response);
uriTB.Text = rootObject.name;
responseDebug.Text = response;
}
but I get the following error:
Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[Erepublik.Citizen+Rank]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.
I can't even parse the value in the main object. Anyway to fix this? and how can I parse a value inside of a nested object? for example: "points" in "rank"
Like the error message says, your rank property in the .NET class is a List<Rank>, but in your JSON it's just a nested object, not an array. Change it to just a Rank instead of a List<Rank>.
Arrays in JSON (or any Javascript, really) are enclosed in []. The {} characters specify a single object. The CLR type has to roughly match the JSON type in order to deserialize. Object to object, array to array.