Getting names of properties in JSON.NET with C# - c#

I've been coding a Minecraft launcher for my personal use (source). I've gotten to the point where I need to parse a JSON file containing all available "profiles" already made by the official launcher. It looks like this:
{
"profiles": {
"1.6.2": {
"name": "1.6.2",
"gameDir": "C:\\Users\\Ilan\\AppData\\Roaming\\.ilan\\1.6.2mc",
"lastVersionId": "1.6.2",
"allowedReleaseTypes": [
"snapshot",
"release"
],
"playerUUID": "e2f423057b72487eb6f7f8ce877a8015"
},
"1.6.1": {
"name": "1.6.1",
"gameDir": "C:\\Users\\Ilan\\AppData\\Roaming\\.ilan\\1.6.1",
"lastVersionId": "1.6.1"
},
"13w38c": {
"name": "13w38c",
"lastVersionId": "13w38c",
"javaArgs": "-Xmx1G",
"allowedReleaseTypes": [
"snapshot",
"release"
],
"playerUUID": "e2f423057b72487eb6f7f8ce877a8015",
"useHopperCrashService": false
},
As you can see, there is an object called "profiles" and in it there are properties with variable names. I want to get the names of those properties, not their values. I don't know how to do so, I tried Value or just the profile .ToString() but both results give me the contents of the property itself, not the name of it. Is there a way to get the names?
Edit: the code that parses the profile JSON is:
string profileJSON = File.ReadAllText(Variables.profileJSONFile);
JObject profiles = JObject.Parse(profileJSON);
MessageBox.Show(profiles["profiles"].ToString());
foreach (JProperty profileAvail in profiles["profiles"])
{
MessageBox.Show(profileAvail.ToString());
}

Use Name property:
Gets the property name.
public string Name { get; }
MessageBox.Show(profileAvail.Name);

IDictionary< string, JToken > json = Jobject.Parse( strJson );
foreach (var kv in json)
{
Console.WriteLine( kv.Key );
}

Related

Add a list in an existing JSON value

So I have below call to a method where my argument is a json string of this type
var jsonWithSearchData = await querySearchData(jsonOut);
jsonOut ->
[
{
"data": {
"_hash": null,
"kind": "ENY",
"id": "t123",
"payload": {
"r:attributes": {
"lok:934": "#0|I"
},
"r:relations": {
"lok:1445": "15318",
"lok:8538": "08562"
},
"r:searchData": "",
"r:type": [
"5085"
]
},
"type": "EQT",
"version": "d06"
}
}
]
The querySearchData() returns me two list something like this :["P123","P124","P987"] and ["Ba123","KO817","Daaa112"]
I want to add this list in my r:searchData key above. The key inside my searchData i.e. r:Porelation and ClassA and ClassB remains static. So I would like my searchData in my input Json to finally become something like this.
"r:searchData": {
"r:Porelation":{
"ClassA": ["P123","P124","P987"],
"ClassB": ["Ba123","KO817","Daaa112"]
}
},
How can I do this? What I tried:
JArray jfinObject = JArray.Parse(jobjects);
jfinObject["r:searchData"]["r:relations"]["ClassA"] = JArray.Parse(ListofCode.ToString());
And I get below error:
System.Private.CoreLib: Exception while executing function: Function1.
Newtonsoft.Json: Accessed JArray values with invalid key value:
"r:searchData". Int32 array index expected.
There are a few ways you can add a node/object/array to existing json.
One option is to use Linq-to-Json to build up the correct model.
Assuming you have the json string described in your question, the below code will add your desired json to the r:searchData node:
var arr = JArray.Parse(json); // the json string
var payloadNode = arr[0]["data"]["payload"];
// use linq-to-json to create the correct object
var objectToAdd = new JObject(
new JProperty("r:Porelation",
new JObject(
new JProperty("r:ClassA", array1),
new JProperty("r:ClassB", array2))));
payloadNode["r:searchData"] = objectToAdd;
where array1 and array2 above could come from a linq query (or just standard arrays).
// Output:
{
"data": {
"_hash": null,
"kind": "ENY",
"id": "t123",
"payload": {
"r:attributes": {
"lok:934": "#0|I"
},
"r:relations": {
"lok:1445": "15318",
"lok:8538": "08562"
},
"r:searchData": {
"r:Porelation": {
"r:ClassA": [
"P123",
"P456"
],
"r:ClassB": [
"Ba123",
"Ba456"
]
}
},
"r:type": [
"5085"
]
},
"type": "EQT",
"version": "d06"
}
}
Online demo
Another option is to create the json from an object, which could be achieved using JToken.FromObject(). However, this will only work if you have property names which are also valid for C# properties. So, this won't work for your desired property names as they contain invalid characters for C# properties, but it might help someone else:
// create JToken with required data using anonymous type
var porelation = JToken.FromObject(new
{
ClassA = new[] { "P123", "P456" }, // replace with your arrays here
ClassB = new[] { "Ba123", "Ba456" } // and here
});
// create JObject and add to original array
var newObjectToAdd = new JObject(new JProperty("r:Porelation", porelation));
payloadNode["r:searchData"] = newObjectToAdd;

JObject parse returns extra curly braces

I have a variable projectData that contains a valid json string that is stored in a database.
After doing the following:
JObject obj = new JObject();
obj = JObject.Parse(projectData);
Instead of getting
{
"devices": {
"device_A": {
"id": "12345",
"name": "test",
"enabled": true
}
}
}
I get this instead:
{{
"devices": {
"device_A": {
"id": "12345",
"name": "test",
"enabled": true
}
}
}}
So basically an aditional { and } were added to my json string.
I have also tried the following:
obj = JsonConvert.DeserializeObject<JObject>(projectData);
and it didnt work.
Why is this a problem to me?
I want to iterate over the obj["devices"] array, and when I do the following
foreach(var d in obj["devices"])
It simply doesnt work because of the double curly braces.
Is there a solution to my problem?
Thank you
{
"devices": {
"device_A": {
"id": "12345",
"name": "test",
"enabled": true
}
}
}
Your json shows devices as a json object and not an array. You cannot iterate over it with a for loop.
You can access the data by parsing it first and then accessing the properties by using the [] brackets.
var obj = JObject.Parse(jsonString);
Console.WriteLine(obj["devices"]["device_A"]["id"].Value<string>());
//prints
12345
// To Loop through multiple devices... you can use this.
foreach (var device in ((JObject)obj["devices"]).Properties())
Console.WriteLine(obj["devices"][device.Name]["id"]);
Also, when you are using the debug mode, the watch or locals will show {{ }} because the braces are escaped.
Array version of Json
If your json looked like below, then you can use the for loop to access the elements of the JArray.
{
"devices": [
{
"device_A": {
"id": "12345",
"name": "test",
"enabled": true
}
}
]
}
with the above json, you can use the for loop in the following way,
var obj = JObject.Parse(json);
foreach (var device in obj["devices"])
Console.WriteLine(device["device_A"]["id"]);
// Prints
12345

Json nested objects deserialization

I would like to dynamically create c# object based on the the consumed Json. I do not care about this json but simply would like pick out properties that I need.
I dont know if its best practice to use dynamic keyword and based on the created object I can create a whole new json structure.
How can I desearilize nested objects. Example json:
{
'name': 'James',
'Profile': {
'body': {
'id': 'Employee1',
'type': 'Employee',
'attributes': {
props...
},
'job': {
props..
},
'team': [],
},
}
Here is a simple attempt.
var test = JsonConvert.DeserializeObject<dynamic>(json);
Console.WriteLine(test.ToString());
I can create object list however when I begin Parent/Child objects I cant seem to get it to work.
Below JSON works fine as an output:
'body': {
'id': 'Employee1',
'type': 'Employee',
'attributes': {
props...
},
'job': {
props..
},
'team': [],
}
Desired Output
{
'name': 'James',
'Profile': {
'body': {
'id': 'Employee1',
'type': 'Employee',
'attributes': {
props...
},
'job': {
props..
},
'team': [],
},
'Profile': {
'body': {
'id': 'Employee2',
'type': 'Employee',
'attributes': {
props...
},
'job': {
props..
},
'team': [],
}
}
How can I deserialize nested objects?
You can use the json class with following this line of code
dynamic data = Json.Decode(json);
You can use JsonConvert:
dynamic myNewObject = JsonConvert.DeserializeObject(json);
And then, you can access json properties like this:
myNewObject.Profile.body.id
No repro. The string is already deserialized. test contains the entire graph. The expected output is produced as long as a valid JSON string is used :
This snippet :
var json=#"{
'name': 'James',
'Profile': {
'body': {
'id': 'Employee1',
'type': 'Employee',
'attributes': {
'a':3
},
'job': {
'a':3
},
'team': [],
},
}";
var x=JsonConvert.DeserializeObject<object>(json);
Console.WriteLiine(x.ToString());
Produces :
{
"name": "James",
"Profile": {
"body": {
"id": "Employee1",
"type": "Employee",
"attributes": {
"a": 3
},
"job": {
"a": 3
},
"team": []
}
}
}
From the comments though, it looks like the actual JSON string isn't a valid JSON string and throws an exception :
I get Newtonsoft.Json.JsonReaderException: 'Additional text encountered after finished reading JSON content: ,. Path '', line 21, position 13.'
This error points to the actual location in the JSON string that caused the problem. Perhaps there's an extra character there? Perhaps the text contains multiple JSON strings instead of one? The following string is not valid JSON - JSON can't have more than one root objects :
{ "a":"b"}
{ "c":"d"}
Logging tools, event processing and analytic software though store multiple JSON strings per file this way because it only needs an append operation, doesn't require parsing the entire text to produce results and makes partitioning easier. That's still invalid JSON though that has to be read and processed one line at a a time.
You many find this described as "streaming JSON", precisely because you can process the data in a streaming fashion. Instead of loading and parsing a 100MB event file, you can load and process a single line at a time eg:
IEnumerable<string> lines=File.ReadLines(pathToBigFile);
foreach(var line in lines)
{
var myObject=JsonConvert.DeserializeObject<someType>(line);
//do something with that object
}
File.ReadLines returns an IEnumerable<string> that only loads a single line on each iteration. Instead of loading the entire 100MB file, only one line is loaded and parsed each time.

How to get sys_id from the result(incident_response) from below c# code

I have the following JSON(modified) which is read from a HttpWebRequest response stream, and I want to extract the value of "sys_id":
{
"result": {
"number": "INC0012618",
"opened_by": {
"link": "//XXX.service-now.com/api/now/v1/table/sys_user/38723e5ddb42f200deb8f6fcbf96196d",
"value": "38723e5ddb42f200deb8f6fcbf96196d"
},
"sys_created_on": "2017-07-20 14:41:52",
"sys_domain": {
"link": "://XXX.service-now.com/api/now/v1/table/sys_user_group/global",
"value": "global"
},
"u_incident_summary": "",
"business_service": {
"link": "://xxx.service-now.com/api/now/v1/table/cmdb_ci_service/IT services",
"value": "IT services"
},
"work_end": "",
"caller_id": {
"link": "://xxxx.service-now.com/api/now/v1/table/sys_user/71f5455d4f735e000d7f0ed11310c719",
"value": "71f5455d4f735e000d7f0ed11310c719"
},
"close_code": "",
"assignment_group": {
"link": "://xxx.service-now.com/api/now/v1/table/sys_user_group/9e158987dba27a007ea0f4e9bf961983",
"value": "9e158987dba27a007ea0f4e9bf961983"
},
"sys_id": "7fb4e50edb8c0b007ea0f4e9bf9619ba",
"u_outage_start_time": ""
}
}
This is what I have tried so far, where incident_responce is a string containing the JSON above:
var jObject = JObject.Parse(incident_responce);
var value = (string)jObject["sys_id"];
Console.WriteLine(value);
But, it didn't work, I think because there is "result" at the start. How can I retrieve this value?
As you suspected, your initial attempt fails because "sys_id" is nested inside the "result" object:
{ "result": { ... "sys_id":"7fb4e50edb8c0b007ea0f4e9bf9619ba" } }
It's easier to see this if you indent and format your JSON, for instance by uploading it to https://jsonformatter.curiousconcept.com/.
Such a nested value can be queried directly by using JToken.SelectToken():
var root = JToken.Parse(incident_responce);
var value = (string)root.SelectToken("result.sys_id");
SelectToken() supports querying for values deep within the JSON container hierarchy using JSONPath syntax. If the "result.sys_id" token is not found, null is returned.
Alternatively, you could use Json.NET's support for querying JSON hierarchies using dynamic functionality:
dynamic root = JToken.Parse(incident_responce);
var value = (string)root.result.sys_id;
However, if the "result" token is not found, a RuntimeBinderException will be thrown instead of a null value returned.
Working .Net fiddle.

How to get the name of a JSON object using? (C# Newtonsoft.JSON)

For those of you familiar with Minecraft, the 1.8 update stores the sounds as a file with an encrypted hash as the name (which you can really just change the extension to .ogg to play). There is an index stored as a JSON file in the assets folder which shows the proper sound name for each file with the encrypted hash name.
I'm trying to create a program that which the user types the name and it will find the sound(s) that contains that name. The index is stored in this fashion:
{ "objects":{"minecraft/sounds/mob/wither/idle2.ogg": {
"hash": "6b2f86a35a3cd88320b55c029d77659915f83239",
"size": 19332
},
"minecraft/lang/fil_PH.lang": {
"hash": "e2c8f26c91005a795c08344d601b10c84936e89d",
"size": 74035
},
"minecraft/sounds/note/snare.ogg": {
"hash": "6967f0af60f480e81d32f1f8e5f88ccafec3a40c",
"size": 3969
},
"minecraft/sounds/mob/villager/idle1.ogg": {
"hash": "a772db3c8ac37dfeb3a761854fb96297257930ab",
"size": 8605
},
"minecraft/sounds/mob/wither/hurt3.ogg": {
"hash": "a4cf4ebe4c475cd6a4852d6b4228a4b64cf5cb00",
"size": 16731
}
For example if the user types wither, it will grab the hashes for "minecraft/sounds/mob/wither/idle2.ogg"
and
"minecraft/sounds/mob/wither/hurt3.ogg"
My question is, how do I get the object names (the names, not the properties) to compare with the user's keyword string.
Sorry if I didn't use proper terminology for some words, I don't tinker with JSON files much. Correct my terminology as needed.
EDIT
This answer solves it a lot more nicely (without dynamic):
https://stackoverflow.com/a/32129497/563532
Original answer:
This works:
var obj = JsonConvert.DeserializeObject<dynamic>(#"{ ""objects"":{""minecraft/sounds/mob/wither/idle2.ogg"": {
""hash"": ""6b2f86a35a3cd88320b55c029d77659915f83239"",
""size"": 19332
},
""minecraft/lang/fil_PH.lang"": {
""hash"": ""e2c8f26c91005a795c08344d601b10c84936e89d"",
""size"": 74035
},
""minecraft/sounds/note/snare.ogg"": {
""hash"": ""6967f0af60f480e81d32f1f8e5f88ccafec3a40c"",
""size"": 3969
},
""minecraft/sounds/mob/villager/idle1.ogg"": {
""hash"": ""a772db3c8ac37dfeb3a761854fb96297257930ab"",
""size"": 8605
},
""minecraft/sounds/mob/wither/hurt3.ogg"": {
""hash"": ""a4cf4ebe4c475cd6a4852d6b4228a4b64cf5cb00"",
""size"": 16731
}
}
}");
var t = obj.objects;
var names = new HashSet<String>();
foreach(JProperty fileThing in t)
{
names.Add(fileThing.Name);
}
names.Dump();
Gives:
minecraft/sounds/mob/wither/idle2.ogg
minecraft/lang/fil_PH.lang
minecraft/sounds/note/snare.ogg
minecraft/sounds/mob/villager/idle1.ogg
minecraft/sounds/mob/wither/hurt3.ogg
You can also do this:
var t = obj.objects;
var names = new Dictionary<String, String>();
foreach(JProperty fileThing in t)
{
names.Add(fileThing.Name, (string)t[fileThing.Name].hash);
}
Which gives you a dictionary linking the original name to the hash:
minecraft/sounds/mob/wither/idle2.ogg -> 6b2f86a35a3cd88320b55c029d77659915f83239
minecraft/lang/fil_PH.lang -> e2c8f26c91005a795c08344d601b10c84936e89d
minecraft/sounds/note/snare.ogg -> 6967f0af60f480e81d32f1f8e5f88ccafec3a40c
minecraft/sounds/mob/villager/idle1.ogg -> a772db3c8ac37dfeb3a761854fb96297257930ab
minecraft/sounds/mob/wither/hurt3.ogg -> a4cf4ebe4c475cd6a4852d6b4228a4b64cf5cb00
Assuming you have a jsonString as a string variable.
jsonString = "";
JArray array = JArray.Parse(json);
foreach (JObject content in array.Children<JObject>())
{
foreach (JProperty prop in content.Properties())
{
Console.WriteLine(prop.Name);
}
}

Categories