I want to parse a json file. My first attempt with a simplified version was only partial successfull.
The simplifiend json structure looks like this
{
"rowCount": 102,
"data": [
{"id": "56", "bezeichnung": "Main Center", "strasse": "foostreet"},
{"id": "34", "bezeichnung": "Side Location", "strasse": "5th aveneue"}
]
}
For the outer json { "rowCount":102, "data":[]} i have a class jsonEnvelope which looks like this
public class JsonEnvelope
{
public int RowCount { get; set; }
public Location[] Data{ get; set; }
}
To parse the json inside the array data "data":[] i have class location which looks like this
public class Location
{
public string id;
public string bezeichnung;
public string strasse;
}
My code to parse the json looks like this
string jsonString = GetJsonFromFile();
var jsonEnvelope = new JsonEnvelope();
var options = new JsonSerializerOptions();
options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
jsonEnvelope = JsonSerializer.Deserialize<JsonEnvelope>(jsonString, options);
foreach (Location h in jsonEnvelope.Data)
{
Console.WriteLine(String.Format("{0} in {1}", h.bezeichnung, h.strasse));
}
Console.WriteLine("row count = " + jsonEnvelope.RowCount);
What is working?
The line jsonEnvelope.RowCount works. The number 102 is written to the console
The foreach is not working the values of h.bezeichnung and h.strasse are not written to the console
Question
while writing my question i figured it out - i will self answer it briefly
The problem was that i used a field in class location instead of property.
Changing the class location to this solved the issue
public class Location
{
public string Id { get; set; }
public string Bezeichnung { get; set; }
public string Strasse { get; set; }
}
Please note that with:
.NET 5 or
System.Text.Json package version 5 added to Core3.1 projects you can use IncludeFields option:
var serializeOptions = new JsonSerializerOptions
{
IncludeFields = true,
};
var o = JsonSerializer.Deserialize<Location>(json, serializeOptions);
Related
I exported from Azure IoT Central to a Blob Storage a file containing several JSON objects (36 objects) within that same file.
The below are the first 2 lines from that file
{"applicationId":"appID","component":"thermostat1","deviceId":"usingTemControllerTemplate","enqueuedTime":"2022-03-21T15:31:38.687Z","enrichments":{},"messageProperties":{},"messageSource":"telemetry","schema":"default#v1","telemetry":{"temperature":23.2},"templateId":"urn:modelDefinition:tczx6jwcwz1:h2httvyo48g"}
{"applicationId":"appID","component":"thermostat2","deviceId":"usingTemControllerTemplate","enqueuedTime":"2022-03-21T15:31:38.703Z","enrichments":{},"messageProperties":{},"messageSource":"telemetry","schema":"default#v1","telemetry":{"temperature":16.9},"templateId":"urn:modelDefinition:tczx6jwcwz1:h2httvyo48g"}
I created 2 classes to show the heirarchy in the JSON objects. RootObject & Telemetry.
public class RootObject
{
public string applicationId { get; set; }
public string component { get; set; }
public string deviceId { get; set; }
public string enqueuedTime { get; set; }
public string messageSource { get; set; }
public string schema { get; set; }
public List<Telemetry> telemetry { get; set; }
public string templateId { get; set; }
}
public class Telemetry
{
public double temperature { get; set; }
}
I followed this answer and modeled it to my specific heirarchy and tried to make it work. However, a JsonReaderException is being thrown I run it in Visual Studio.
This is the code I'm running:
using Newtonsoft.Json;
string filePath = "~pathToFile";
RootObject rt = JsonConvert.DeserializeObject<RootObject>(filePath);
if (rt.telemetry[1].temperature == 23.2)
{
Console.WriteLine(rt.telemetry[1].temperature);
}
The JsonReaderException is being thrown on this line:
RootObject rt = JsonConvert.DeserializeObject<RootObject>(filePath);
In the below image is the message being shown:
Could someone please help me find the cause of this issue and how I could resolve it?
This file is not a list/array of objects, it's a 36 lines with each line containing json for a single object.
With this observation we can:
List<RootObject> list = new();
foreach(var line in lines.Where( l => !string.IsNullOrWhiteSpace(l)))
{
RootObject? o = JsonConvert.DeserializeObject<RootObject>(line);
if (o != null)
{
list.Add(o);
}
}
telmetry is an object, not a list so you need to also change the RootObject definition:
"telemetry": {
"temperature": 23.2
},
public class RootObject
{
...
public Telemetry telemetry { get; set; }
first of all the JSON format is wrong, it looks like this (see below) and
secondly he doesn't want to have the File path but the json (value), so you have to read it in
and what is also very important. You have 2 elements of "RootObject",
that means you have to put into a Array or List<> of RootObjects
Code:
using Newtonsoft.Json;
//reads the file and saves into a string
string jsonValue = File.ReadAllText("~pathToFile");
//Deserialize the objects and put them in a list of "RootObject".
List<RootObject> rt = JsonConvert.DeserializeObject<List<RootObject>>(jsonValue);
correct JSON format:
[
{
"applicationId": "appID",
"component": "thermostat1",
"deviceId": "usingTemControllerTemplate",
"enqueuedTime": "2022-03-21T15:31:38.687Z",
"enrichments": {},
"messageProperties": {},
"messageSource": "telemetry",
"schema": "default#v1",
"telemetry": {
"temperature": 23.2
},
"templateId": "urn:modelDefinition:tczx6jwcwz1:h2httvyo48g"
},
{
"applicationId": "appID",
"component": "thermostat2",
"deviceId": "usingTemControllerTemplate",
"enqueuedTime": "2022-03-21T15:31:38.703Z",
"enrichments": {},
"messageProperties": {},
"messageSource": "telemetry",
"schema": "default#v1",
"telemetry": {
"temperature": 16.9
},
"templateId": "urn:modelDefinition:tczx6jwcwz1:h2httvyo48g"
}
]
you have to fix json, by converting it to array of objects
var json = File.ReadAllText(filePath);
json = "[" + json.Replace("\n\r",",")+"]";
List<RootObject> lrt = JsonConvert.DeserializeObject<List<RootObject>>(json);
double[] telemetries=rt.Select(r => r.telemetry.temperature ).ToArray(); // [23.2,16.9]
double telemetry=rt.Where(t=> t.telemetry.temperature==23.2)
.Select(r =>r.telemetry.temperature ).FirstOrDefault(); //23.2
and fix class too, telemetry should be an object, not a list
public class RootObject
{
....
public Telemetry telemetry { get; set; }
}
I have data bind to an object. Now I want to serialize it with a custom property name.
[JsonProperty(PropertyName = "KLM")]
public string ABC { get; set; } = "Test1";
[JsonProperty(PropertyName = "NOP")]
public string DEF { get; set; } = "Test2";
[JsonProperty(PropertyName = "QRS")]
public string GHI { get; set; } = "Test3";
When I serialize this object I want below serialization
{ "KLM" : "Test1", "NOP" : "Test2", "QRS" : "Test3" }
instead of
{ "ABC" : "Test1", "DEF" : "Test2", "GHI" : "Test3" }
and when deserialize it with the below JSON it should work just fine
{ "ABC" : "1Test", "DEF" : "2Test", "GHI" : "3Test" }
This question might have been asked many times, but I couldn't find the appropriate answer.
Please try that:
using Newtonsoft.Json;
namespace Application.ConsoleApp
{
class Program
{
static void Main(string[] args)
{
var j = "{ \"KLM\" : \"Test1\", \"NOP\" : \"Test2\", \"QRS\" : \"Test3\" }";
var o = JsonConvert.DeserializeObject<MyJsonObject>(j);
var s = JsonConvert.SerializeObject(o);
}
}
public class MyJsonObject
{
[JsonProperty(PropertyName = "KLM")]
public string ABC { get; set; } = "Test1";
[JsonProperty(PropertyName = "NOP")]
public string DEF { get; set; } = "Test2";
[JsonProperty(PropertyName = "QRS")]
public string GHI { get; set; } = "Test3";
}
}
You might be using different tools for attributes and serialization / deserialization. In my example, you can see that I am using the Newtonsoft library. I am taking attributes and methods from this library. For example, if you use attributes from Newtonsoft and methods from System.Text.Json, it won't work the way you want.
UPD:
Isn't that what you want?
The image shows that this works both ways. If json matches attributes then it is desirialized by attributes. If json matches the names of the properties of the class, then json is deserialized by the names of the properties. This object is always desirialized by attributes.
Sorry if I still misunderstand you!
I have this Json:
{
"UpdatePack":"updatePacks\/1585654836.pack",
"Updates":[
{
"Name":"MsgBoxEx",
"version":"1.5.14.88",
"ChangeLog":"BugFix: Form didn't resize correct.",
"Hash":"5FB23ED83693A6D3147A0485CD13288315F77D3D37AAC0697E70B8F8C9AA0BB8"
},
{
"Name":"Utilities",
"version":"2.5.1.58",
"ChangeLog":"StringManagement updated.",
"Hash":"05E6B3F521225C604662916F50A701E9783E13776DE4FCA27BE4B69705491AC5"
}
]
}
I have created 2 classes to be used to Deserialize it.
class UpdatesList
{
public string Name { get; set; }
public string Version { get; set; }
public string ChangeLog { get; set; }
public string Hash { get; set; }
}
class JsonObjectHolder
{
public string UpdatePack { get; set; }
//public Dictionary<int, MyData> { get; set; }
public Dictionary<int, UpdatesList> Updates { get; set; }
}
But when I try to access the dictionary, I keep getting Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object. at " Console.WriteLine(jsonTest.Dict.Count);"
Am I Deserializing it wrong, or do I need to do some thing else to access the result of the dictionary?
I'm new to both C# and Json.
I hope that some one could point me in the right direction on how to handle this.
I'm using Visual Studio 2019 latest update, and .net 4.8.
Regards
/LR
You code doesn't work because 0 and 1 tokens just a properties, not the array items (you don't have square brackets [] around them). You can parse these values to desired structure manually using JObject
var json = JObject.Parse(your_json_string);
var dict = new Dictionary<int, UpdatesList>();
foreach (var item in json.Properties())
{
if (item.Value.Type == JTokenType.Object)
{
var index = int.Parse(item.Name);
var updateList = item.Value.ToObject<UpdatesList>();
dict.Add(index, updateList);
}
}
var holder = new JsonObjectHolder
{
UpdatePack = json["Updates"]?.Value<string>(),
Dict = dict
};
Update: According to OP changes made to JSON it might be deserialized even more simply
var list = json["Updates"]?.ToObject<List<UpdatesList>>();
var holder = new JsonObjectHolder
{
UpdatePack = json["UpdatePack"]?.Value<string>(),
Dict = list.Select((updatesList, index) => new { updatesList, index })
.ToDictionary(x => x.index, x => x.updatesList)
};
The main point here is that Updates is an array of items, not the key-value collection. It can be transformed into Dictionary<int, UpdatesList> using ToDictionary method from System.Linq (or just use List<UpdatesList> as is)
The exception you're getting essentially means the value is being accessed before the object is initialized.
A better, simpler and cleaner way to doing it is using NewtonSoft. (you can easily get it as a Nuget package)
example:
public class Account
{
public string Email { get; set; }
public bool Active { get; set; }
public DateTime CreatedDate { get; set; }
public IList<string> Roles { get; set; }
}
and then usage:
string json = #"{
'Email': 'james#example.com',
'Active': true,
'CreatedDate': '2013-01-20T00:00:00Z',
'Roles': [
'User',
'Admin'
]
}";
Account account = JsonConvert.DeserializeObject<Account>(json);
Console.WriteLine(account.Email);
Source: https://www.newtonsoft.com/json/help/html/DeserializeObject.htm
I don't see why you need Dictionary<int, UpdatesList> Updates, when you can easily just use List<Update> Updates, since your updates are in a JSON array.
I would model your classes like this:
public class Update
{
public string Name { get; set; }
public string Version { get; set; }
public string ChangeLog { get; set; }
public string Hash { get; set; }
}
public class RootObject
{
public string UpdatePack { get; set; }
public List<Update> Updates { get; set; }
}
You can then deserialize with:
JsonConvert.DeserializeObject<RootObject>(json);
Try it out on dotnetfiddle.net
Note: To convert JSON to C# classes, you can go to Edit -> Paste Special -> Paste JSON as Classes inside Visual Studio. Make sure you have copied the JSON to your clipboard before using it. You will get classes similar to above.
your data and the class is not compatible. if you change the string like this it would work.
change "Updates" to "UpdatePack" and add "Dict" around the dictionary items.
{
"UpdatePack":"updates\/4D1D7964D5B88E5867324F575B77D2FA.zip",
"Dict":{
"0":{
"Name":"MsgBoxEx",
"Version":"1.0.123.58",
"ChangeLog":"Bugfix:Form didn't resize correct",
"hash":"AA94556C0D2C8C73DD217974D252AF3311A5BF52819B06D179D17672F21049A6"
},
"1":{
"Name":"Utilities",
"Version":"1.5.321.87",
"ChangeLog":"StringManagement updated",
"hash":"2F561B02A49376E3679ACD5975E3790ABDFF09ECBADFA1E1858C7BA26E3FFCEF"
}
}
}
I have JSON like this:
{
'surveys': [
{
'title': 'first',
'id': 100,
},
{
'title': 'second',
'id': 101,
},
{
'title': 'third',
'id': 102,
},
]
}
I want to have the output like this:
title: first
title: second
title: third
and my program in C# is like this:
WebClient client = new WebClient();
var json = client.DownloadString("http://www.test.com/api/surveys/?api_key=123");
Debug.WriteLine(json); //write all data from json
//add
var example = JsonConvert.DeserializeObject<Example>(json);
Debug.WriteLine(example.Data.Length);
class Example
{
public surveys[] Data { get; set; }
}
class surveys
{
public string title { get; set; }
public int id { get; set; }
}
I get this error:
Thrown: "Object reference not set to an instance of an object." (System.NullReferenceException) Exception Message = "Object reference not set to an instance of an object.", Exception Type = "System.NullReferenceException", Exception WinRT Data = ""
at this line: Debug.WriteLine(example.Data.Length);
where is the problem?
One problem I see is that your outer class has a property named Data, which is an array of 'surveys' objects, but your Json has a list of 'surverys' objects under the property 'surveys'. Hence the 'Data' property is never populated.
Consider the following C# class structure:
class Example
{
public survey[] surveys{ get; set; }//Data renames to surveys
}
class survey //Singular
{
public string title { get; set; }
public int id { get; set; }
}
Why can't you do so?:
JObject data = JObject.Parse(json);
foreach (var survey in data["surveys"].Children())
{
Debug.WriteLine("title: " + survey["title"]);
}
You need to use JSON.Net and use the class JsonConvert and the method DeserializeObject<T>.
If you run this:
JsonConvert.DeserializeObject<JObject>();
Then you will get back a list of de-serialized JObject objects.
Use, NuGet to download the package. I think it is called JSON.net.
Here is the weblink
WebClient client = new WebClient();
var json = client.DownloadString("http://www.test.com/api/surveys/?api_key=123");
Debug.WriteLine(json); //write all data from json
//add
var example = JsonConvert.DeserializeObject<Survey>(json);
Debug.WriteLine(example.length); // this could be count() instead.
class Survey
{
public string title { get; set; }
public int id { get; set; }
}
This should work!
Use json2csharp to generate c# classes from json.
You will also need to use Json.NET.
public class Survey
{
public string title { get; set; }
public int id { get; set; }
}
public class RootObject
{
public List<Survey> surveys { get; set; }
}
Then you can do:
var client = new WebClient();
string json = client.DownloadString(some_url);
RootObject root = JsonConvert.DeserializeObject<RootObject>(json);
foreach (Survey s in root.surveys)
{
// Do something with your survey
}
Don't forget to use Newtonsoft.Json namespace once you add a reference to it within your project.
using Newtonsoft.Json;
Edit: I have tested it using:
string json = "{'surveys': [{'title': 'first','id': 100,},{'title': 'second','id': 101,},{'title': 'third','id': 102,},]}";
instead of using the WebClient, and it works.
Below is a section of json I receive from an endpoint.
If you look at the Json below, 'User-Defined-Network-Name' is a custom node and the name will change each time.
How do I define a C# object for this Json?
"addresses": {
"public": [{
"version": 6,
"address": "2005:4600:788e:0910:1a72:81c0:ff03:c7y6"
},
{
"version": 4,
"address": "197.68.xx.xxx"
}],
"private": [{
"version": 4,
"address": "10.xx.xx.xxx"
}],
"User-Defined-Network-Name": [{
"version": 4,
"address": "192.xxx.x.xxx"
}]
}
This is how far I have come -
[Serializable]
public class Addresses
{
public List<Public> #public { get; set; }
public List<Private> #private { get; set; }
}
Im using 'JavascriptSerializer' class to deserialize json.
Thanks,
Ryan
addresses can be deserialized to a type like Dictionary<string,List<YourClass>> where YourClass holds version and addresss.
var obj = new JavaScriptSerializer().Deserialize<Root>(jsonstring);
--
public class Root
{
public Dictionary<string,List<VersionAddress>> addresses;
//Your other fields/properties
}
public class VersionAddress
{
public string version;
public string address;
}
You could take advantage of the dynamic nature of C#:
// this could come from user input:
string userDefinedName = "User-Defined-Network-Name";
string json = "YOUR JSON COMES HERE";
var serializer = new JavaScriptSerializer();
dynamic result = serializer.DeserializeObject(json);
int version = result["addresses"][userDefinedName][0]["version"];
string address = result["addresses"][userDefinedName][0]["address"];
Console.WriteLine(version);
Console.WriteLine(address);
and if you wanted to loop through the results:
foreach (dynamic item in result["addresses"][userDefinedName])
{
int version = item["version"];
string address = item["address"];
Console.WriteLine(version);
Console.WriteLine(address);
}
Why don't you make network names a dictionary, with key of network name ?
Then you can just iterate over it.
I would not recommend using JavaScriptSerializer, as it has been deprecated. If you want a third-party solution, JSON.Net is pretty good from what I hear.
However, I'm one that's weird about dependencies, so I typically roll my own if it doesn't exist already. Fortunately, this one isn't too hard due to DataContractJsonSerializer from the System.Runtime.Serialization namespace.
All you need to do is first define all the objects in a nested fashion:
using System.Reflection;
using System.Runtime.Serialization; // You will have to add a reference
using System.Runtime.Serialization.Json; // to System.Runtime.Serialization.dll
[DataContract]
public class AddressInfo
{
[DataMember(Name = "address")]
public string Address { get; set; }
[DataMember(Name = "version")]
public int Version { get; set; }
}
[DataContract]
public class AddressList
{
[DataMember(Name = "public")]
public IEnumerable<AddressInfo> Public { get; set; }
[DataMember(Name = "private")]
public IEnumerable<AddressInfo> Private { get; set; }
[DataMember(Name = "User-Defined-Network-Name")]
public IEnumerable<AddressInfo> UserDefined { get; set; }
}
Then a couple helper methods to do the deserialization:
// This will change the DataMember.Name at runtime!
// This will only work if you know the node name in advance.
static void SetUserDefinedNodeName(string userDefinedNodeName)
{
var type = typeof(AddressList);
var property = type.GetProperty("UserDefined", BindingFlags.Default);
var attribute = property.GetCustomAttribute<DataMemberAttribute>();
if (attribute != null)
attribute.Name = userDefinedNodeName;
}
static T Deserialize<T>(string jsonText, string userDefinedNodeName)
{
SetUserDefinedNodeName(userDefinedName);
var jsonBytes = Encoding.UTF8.GetBytes(jsonText);
using (var stream = new MemoryStream(jsonBytes))
{
var serializer = new DataContractJsonSerializer(typeof(T));
var obj = serializer.ReadObject(stream) as T;
return obj;
}
}
Then you use it like so:
var jsonText = // get your json text somehow
var addressList = Deserialize<AddressList>(jsonText);