I want to take some Json and parse it in to a collection of key/value pairs, but some of the values will be dictionaries themselves. I tried the usual Newtonsoft deserialization. It's close, but not quite right. The end result must be a dictionary, not a strongly typed class.
This is some example Json:
{
"JobNumber": 1010,
"Asset": null,
"JobNotes": [
{
"NoteText": "It's not working.",
"NoteType": "Complaint"
},
{
"NoteText": "Needs to be fixed",
"NoteType": "Job"
}
]
}
This is the code I used to deserialize:
var json = File.ReadAllText(#"c:\temp\job.json");
var result = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
The result is almost correct, but the value of the item with a key of "JobNotes" is just json string. I want the parser to recurse in and deserialise the inner Json to a further dictionary of strings and objects. Is there a way I can do this with the Newtonsoft library? Or, is there another library that will do the trick? Can I hook in to the parsing method to override the functionality at that point in time?
This is a modified version of #DanielKeogh's code. It works well.
class Program
{
static void Main(string[] args)
{
var json = File.ReadAllText(#"c:\temp\job3.json");
var result = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
RecurseDeserialize(result);
}
private static void RecurseDeserialize(Dictionary<string, object> result)
{
//Iterate throgh key/value pairs
foreach (var keyValuePair in result.ToArray())
{
//Check to see if Newtonsoft thinks this is a JArray
var jarray = keyValuePair.Value as JArray;
if (jarray != null)
{
//We have a JArray
//Convert JArray back to json and deserialize to a list of dictionaries
var dictionaries = JsonConvert.DeserializeObject<List<Dictionary<string, object>>>(jarray.ToString());
//Set the result as the dictionary
result[keyValuePair.Key] = dictionaries;
//Iterate throught the dictionaries
foreach (var dictionary in dictionaries)
{
//Recurse
RecurseDeserialize(dictionary);
}
}
}
}
}
This modified Json shows how deep it goes:
{
"JobNumber": 1010,
"Asset": null,
"JobNotes": [
{
"NoteText": "It's not working.",
"NoteType": "Complaint"
},
{
"NoteText": "Needs to be fixed",
"NoteType": "Job",
"JobNoteNotes": [
{
"Something": 1,
"Something2": "Test"
}
]
}
]
}
The result ends three dictionaries deep so that I can get at the "Something" value by key.
This can be done with a little recursion. I'll leave defining IsJson up to you as an academic exercise. :)
Dictionary<string, object> RecursiveDeserialize(string json)
{
var result = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
foreach (var pair in result.ToArray())
{
if(IsJson(pair.Value))
{
result[pair.Key] = RecursiveDeserialize(pair.Value);
}
}
return result;
}
Using this object for json string
public class JobNote
{
public string NoteText { get; set; }
public string NoteType { get; set; }
}
public class ListJob
{
public int JobNumber { get; set; }
public object Asset { get; set; }
public List<JobNote> JobNotes { get; set; }
}
Then you can deserialize it
Related
I have the below Json Array and I need to get the value of the key inside my "relation" object. How do I do that? The relation object has different set of key-value pair for each object in the array.
[
{
"data": {
"_hash": null,
"kind": "ENTITY",
"id": "test122",
"payload": {
"attributes": {
"bbl:27": false
},
"relations": {
"bbl:45": "P18",
"bbl:P18": "P562"
},
"type": [
"P185"
]
}
}
}
]
In above inside relations the keys are "bbl:45" and "bbl:P18". I need these values, in specific I need to extract 45 and P18, considering bbl remain constant inside relation object. how do i do it. There are multiple objects in the Jarray, I have shown only one.
I would mention, if you can change data format, consider doing so, having payload as part as a member proper name is highly unusual but you could do like this:
[Fact]
public void DynamicJsonHandlingTest()
{
var serialized = "[{\"data\":{\"_hash\":null,\"kind\":\"ENTITY\",\"id\":\"test122\",\"payload\":{\"attributes\":{\"bbl:27\":false},\"relations\":{\"bbl:45\":\"P18\",\"bbl:P18\":\"P562\"},\"type\":[\"P185\"]}}}]";
using var jDoc = JsonDocument.Parse(serialized);
var enumerator = jDoc.RootElement.EnumerateArray();
foreach(var JsonElement in enumerator)
{
var relationsElement = JsonElement.GetProperty("data").GetProperty("payload").GetProperty("relations");
foreach (var ele in relationsElement.EnumerateObject())
{
var sought = ele.Name.Split(":")[1];
//sought now cointains first 45 then P18
}
}
}
The following Code solves your problem.
using Newtonsoft.Json.Linq;
using System;
using System.Linq;
using Json.Net;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
string json = "{\"data\":{\"_hash\":null,\"kind\":\"ENTITY\",\"id\":\"test122\",\"payload\":{\"attributes\":{\"bbl:27\":false},\"relations\":{\"bbl:45\":\"P18\",\"bbl:P18\":\"P562\"},\"type\":[\"P185\"]}}}";
JObject jsonObject = JObject.Parse(json);
var results = jsonObject["data"]["payload"]["relations"];
foreach (var item in results)
{
string propName = ((JProperty)item).Name;
string requiredValue = propName.Split(':')[1];
Console.WriteLine($"Required: {requiredValue} !");
}
}
}
}
You should add some error handling when the property name does not contains :
For dynamic keys you can use dictionary:
public class Root
{
public Data data { get; set; }
}
public class Data
{
public Payload payload { get; set; }
}
public class Payload
{
public Dictionary<string, string> relations { get; set; }
}
var result = JsonConvert.DeserializeObject<List<Root>>(sourcejson);
And then next will contain all keys:
var keys = result
.Select(r => r.data)
.Select(r => r.payload)
.SelectMany(r => r.relations.Keys)
Or use the LINQ to JSON API:
var result = JsonConvert.DeserializeObject<JArray>(sourcejson);
var list = result
.Children()
.SelectMany(c => c["data"]["payload"]["relations"].Children<JProperty>())
.Select(p => p.Name)
.ToList(); // list is {"bbl:45", "bbl:P18"}
And then you can process the keys as you need - for example using Split(':') to turn "bbl:45" into array of "bbl" and `"45".
I have JSON data that I need to parse from C# object.
this is JSON Example.
{
"types":
[
[
"tour_type",
[
["groups",1],
["individual",2]
]
]
]
}
Here are my C# classes that are meant to contain that data:
using System;
using Newtonsoft.Json;
namespace JsonDeserializationTest
{
[JsonProperty("types")]
public class Types
{
[JsonProperty]
public List<Type> Values {get;set;}
}
public class Type
{
[JsonProperty]
public string Key {get;set;}
[JsonProperty]
public List<Dictionary<string, int>> Values { get; set; }
}
}
It's not working now.
How can I fix it?
Use the JsonSerializer (System.Text.Json) object.
Code:
YourClass obj = JsonSerializer.Deserialize<YourClass>(jsonString);
Your json has a list of list of the object... but you are declaring only List of the object.
public class Types
{
[JsonProperty("types")]
public List<List<object>> Values { get; set; }
// ------ UPDATE: This can only be list of list of 'object' ------- \\
}
Also, you are using the JsonProperty on the class, which is not where that normally goes. You want to use that on the property of the class.
UPDATE:
You cannot use List<List<Type>> for the json you are getting, it can only be List<List<object>>. You have to use object because it can either be a string or a List<List<string>>. After you update your Types class, you can successfully deserialize the json above.
var obj = JsonConvert.DeserializeObject<Types>(json);
and based on your json definition, you can access tour_type by using the following code
types.Values.First()[0].ToString()
// output: tour_type
List<List<string>> data = JsonConvert.DeserializeObject<List<List<string>>>(types.Values.First()[1].ToString())
// data[0]
[0]: "groups"
[1]: "1"
// data[1]
[0]: "individual"
[1]: "2"
Since both of the items in the types are objects, you will either have to convert them to string or a list of list of strings or whatever object they actually are.
The JSON payload in the provided example is formatted quite strangely, especially since it contains seemingly unnecessary array nesting. A payload like this usually includes more nested objects (rather than a bunch of nested arrays). Additionally, it has a list of (string, int) pairs, which is semantically very similar to a Dictionary<string, int>, but the payload doesn't lend itself to that. It would be helpful to know where it is coming from (what context) to understand how it might change.
The example JSON brings up a few questions (that you may want to ask yourself):
Can the "types" array contain multiple entries (at its immediate nesting)?
Can the "tour_type" key name appear after the array of string, int pairs? Is it possible for an entry where no such name exists?
What other elements can exist in the arrays within "tour_type"?
Is it guaranteed that the most nested array will contain just a single (string, int) pair?
Similarly, it is hard to understand what the example C# class is trying to encapsulate. Is List<Dictionary<string, int>> necessary?
All that said, here's a solution using the built-in System.Text.Json library, that could work for you. You could write something similar using Newtonsoft.Json, if necessary. The solution assumes:
We can't change the JSON payload (and that the third party API response will always returns something that is structurally similar to the example)
We can only make minimal changes to the C# class object provided in the example
The solution creates and a JsonConverter<T> that uses the low-level Utf8JsonReader to manually parse and create the custom object. This is required since nested "[" are being used to delineate what should be objects rather than "{". The converter is then registered by annotating the class with the attribute. Now, simply call JsonSerializer.Deserialize, passing in the JSON payload.
public class Tours
{
[JsonPropertyName("types")]
public List<UserType> Types { get; set; }
}
// Annotate the type to register the converter to use
[JsonConverter(typeof(CustomUserTypeConverter))]
public class UserType
{
public string Key { get; set; }
public Dictionary<string, int> Values { get; set; }
}
// This will use the low-level reader to build up the UserType
public class CustomUserTypeConverter : JsonConverter<UserType>
{
// Extra structural validation was done for invalid/incomplete JSON
// which might be too strict or incorrect and hence might require adjustments.
public override UserType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var result = new UserType();
if (!reader.Read())
{
throw new JsonException("Incomplete JSON.");
}
if (reader.TokenType != JsonTokenType.EndArray)
{
result.Key = reader.GetString();
ReadAndValidate(ref reader, JsonTokenType.StartArray);
int depthSnapshot = reader.CurrentDepth;
var values = new Dictionary<string, int>();
do
{
reader.Read();
if (reader.TokenType != JsonTokenType.StartArray && reader.TokenType != JsonTokenType.EndArray)
{
throw new JsonException($"Invalid JSON payload. Expected Start or End Array. TokenType: {reader.TokenType}, Depth: {reader.CurrentDepth}.");
}
if (reader.CurrentDepth <= depthSnapshot)
{
break;
}
reader.Read();
if (reader.TokenType != JsonTokenType.EndArray)
{
string key = reader.GetString();
reader.Read();
int value = reader.GetInt32();
values.Add(key, value);
ReadAndValidate(ref reader, JsonTokenType.EndArray);
}
} while (true);
ReadAndValidate(ref reader, JsonTokenType.EndArray);
result.Values = values;
}
return result;
}
private void ReadAndValidate(ref Utf8JsonReader reader, JsonTokenType expectedTokenType)
{
bool readNext = reader.Read();
if (!readNext || reader.TokenType != expectedTokenType)
{
string message = readNext ?
$"Invalid JSON payload. TokenType: {reader.TokenType}, Depth: {reader.CurrentDepth}, Expected: {expectedTokenType}" :
$"Incomplete JSON. Expected: {expectedTokenType}";
throw new JsonException(message);
}
}
// Implement this method if you need to Serialize (i.e. write) the object
// back to JSON
public override void Write(Utf8JsonWriter writer, UserType value, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
}
Here's how you would use the above converter to serialize the JSON string provided in the example, along with how to access the values.
public static Tours ParseJson(string json)
{
Tours tours = JsonSerializer.Deserialize<Tours>(json);
return tours;
}
public static void AccessValues(Tours tours)
{
foreach (UserType data in tours.Types)
{
string typeName = data.Key; // "tour_type"
foreach (KeyValuePair<string, int> pairs in data.Values)
{
string key = pairs.Key; // "groups" or "individual
int value = pairs.Value; // 1 or 2
}
}
}
For what it's worth, Visual Studio suggests the following C# class structure for the example JSON (which is similar to what #Jawad suggested):
public class Rootobject
{
public object[][] types { get; set; }
}
Hope that helps.
I couldn't figure out your JSON so I created an example with verified JSON.
Try this:
JSON:
{
"Items": [
{
"Name": "tour",
"Attributes": [
{
"Name": "groups",
"Value": 1
},
{
"Name": "individual",
"Value": 2
}
]
},
{
"Name": "demo",
"Attributes": [
{
"Name": "this is demo",
"Value": 3
},
{
"Name": "design pattern",
"Value": 99
}
]
}
]
}
Types foo = JsonSerializer.Deserialize<Types>(jsonString);
public class TypeAttribute
{
public string Name { get; set; }
public int Value { get; set; }
}
public class Type
{
private readonly ICollection<TypeAttribute> _attributes;
public Type()
{
_attributes = new Collection<TypeAttribute>();
}
public void AddAttributes(IEnumerable<TypeAttribute> attrs)
{
foreach(TypeAttribute ta in attrs)
{
_attributes.Add(ta);
}
}
public string Name { get; set; }
public IEnumerable<TypeAttribute> Attributes
{
get { return _attributes; }
set
{
foreach(TypeAttribute ta in value)
{
_attributes.Add(ta);
}
}
}
}
public class Types
{
ICollection<Type> _items;
public Types()
{
_items = new Collection<Type>();
}
public void AddItems(IEnumerable<Type> tps)
{
foreach (Type t in tps)
{
_items.Add(t);
}
}
public IEnumerable<Type> Items
{
get { return _items; }
set
{
foreach (Type t in value)
{
_items.Add(t);
}
}
}
}
I need to deserialize this weird JSON (image below). I've seen some deserialization hints using Dictionary<>, etc. but the problem is that "parameters" contains different data, then previous keys.
Can I somehow get it to work using JsonSerializer deserializator without doing foreach loops and other suspicious implementations? I do need data from "data" in my application.
Here's some of my code:
using var client = new WebClient();
var json = client.DownloadString(GetJsonString());
var invoicesData = JsonSerializer.Deserialize<JsonMyData>(json, options);
If using Newtonsoft is necessary I might start using it.
With Newtonsoft you can parse and access arbitrary JSON documents, even ones that can't reasonably be deserialized into a .NET object. So something like:
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
namespace ConsoleApp35
{
class Program
{
static void Main(string[] args)
{
var json = #"
{
""myData"" :
{
""0"" : { ""data"": { ""A"":1,""B"":2} },
""1"" : { ""data"": { ""A"":1,""B"":2} },
""2"" : { ""data"": { ""A"":1,""B"":2} },
""3"" : { ""data"": { ""A"":1,""B"":2} },
""parameters"" : { ""p"":""a""}
},
""status"":{ }
}";
var foo = JObject.Parse(json);
var a = foo["myData"]["1"]["data"];
Console.WriteLine(a);
Console.WriteLine("Hit any key to continue");
Console.ReadKey();
}
}
}
I think you should really consider using Newtonsoft.Json instead of default JsonDeserializer, it is much easier to use in such situations.
If you are interested in processing this without foreach loops and wanting to access the data in a list format, I would suggest using Dictionary for this. When you use dictionary, you can use Objects as values that would compensate for differences in numbers (0, 1, 2, ..) and words (parameters).
// Classes to Deserialize data we need.
public class MyObject
{
[JsonProperty("data")]
public Data Data { get; set; }
}
public class Data
{
public int A { get; set; }
public int B { get; set; }
}
Usage in Main
// Read in the JSON
var myData = JsonConvert.DeserializeObject<dynamic>(jsonString)["myData"];
// Convert To Dictionary
Dictionary<string, dynamic> dataAsObjects = myData.ToObject<Dictionary<string, dynamic>>();
string searchFor = "3";
dataAsObjects.TryGetValue(searchFor, out dynamic obj);
if (obj != null)
{
// Conversion to int and matching against searchFor is to ensure its a number.
int.TryParse(searchFor, out int result);
if (result == 0 && result.ToString().Equals(searchFor))
{
MyObject myObject = obj.ToObject<MyObject>();
Console.WriteLine($"A:{myObject.Data.A} - B:{myObject.Data.B}");
}
else if (result == 8 && result.ToString().Equals(searchFor))
{
// I am not clear on whats your parameters class look like.
MyParameters myParams = obj.ToObject<MyParameters>();
}
}
Output
A:1 - B:2
With this method you can either access the numbers or the parameters element.
Can somebody help me to parse the json and get the details.
Lets say i have Top2 input parameter as Police and i want to know the respective Top3 and in that Top3 array i need to check whether Test1Child value is there or not.
I am using newtonsoft json + c# and so far i can get till DeviceTypes using below code
var json = File.ReadAllText(jsonFile); // below json is stored in file jsonFile
var jObject = JObject.Parse(json);
JArray MappingArray =(JArray)jObject["Top1"];
string strTop1 = ObjZone.Top1.ToString();
string strTop2 = ObjZone.Top2.ToString();
var JToken = MappingArray.Where(obj => obj["Top2"].Value<string>() == strTop2).ToList();
//Json
{
"Top1": [
{
"Top2": "Test1",
"Top3": [
"Test1Child"
]
},
{
"Top2": "Test2",
"Top3": [
"Test2Child"
]
}
]
}
I'd use http://json2csharp.com/ (or any other json to c# parser) and then use C# objects (it's just easier for me)
This would look like that for this case:
namespace jsonTests
{
public class DeviceTypeWithResponseTypeMapper
{
public string DeviceType { get; set; }
public List<string> ResponseTypes { get; set; }
}
public class RootObject
{
public List<DeviceTypeWithResponseTypeMapper> DeviceTypeWithResponseTypeMapper { get; set; }
}
}
and the use it like that in code:
var rootob = Newtonsoft.Json.JsonConvert.DeserializeObject<RootObject>(str);
var thoseThatHaveNotUsed = rootob.DeviceTypeWithResponseTypeMapper.Where(dtwrtm =>
dtwrtm.ResponseTypes.Any(rt => rt == "NotUsed"));
foreach (var one in thoseThatHaveNotUsed)
{
Console.WriteLine(one.DeviceType);
}
this code lists all the Device types that have "NotUsed" among the responses.
version 2 (extending your code) would look like that, i believe:
static void Main(string[] args)
{
var json = str; // below json is stored in file jsonFile
var jObject = JObject.Parse(json);
JArray ZoneMappingArray = (JArray)jObject["DeviceTypeWithResponseTypeMapper"];
string strDeviceType = "Police";
string strResponseType = "NotUsed";
var JToken = ZoneMappingArray.Where(obj => obj["DeviceType"].Value<string>() == strDeviceType).ToList();
var isrespTypeThere = JToken[0].Last().Values().Any(x => x.Value<string>() == strResponseType);
Console.WriteLine($"Does {strDeviceType} have response type with value {strResponseType}? {yesorno(isrespTypeThere)}");
}
private static object yesorno(bool isrespTypeThere)
{
if (isrespTypeThere)
{
return "yes!";
}
else
{
return "no :(";
}
}
result:
and if you'd like to list all devices that have response type equal to wanted you can use this code:
var allWithResponseType = ZoneMappingArray.Where(jt => jt.Last().Values().Any(x => x.Value<string>() == strResponseType));
foreach (var item in allWithResponseType)
{
Console.WriteLine(item["DeviceType"].Value<string>());
}
I am calling a REST api that returns their data in the following format:
{
"facets": [
{
"M": 100
},
{
"F": 210
}
]
}
I am not sure how to define a C# class that maps to this JSON since the M/F property name could be anything. This is currently a facet for gender, but for something else like language it might be "English", "Spanish", "Japanese", etc. Ideally I would like something like a dictionary.
Where the keys can vary, use a dictionary to represent the object:
public class Criteria
{
public List<Dictionary<string, int>> facets { get; set; }
}
(If the dictionary value isn't always an int, use object instead.)
Fiddle: https://dotnetfiddle.net/IwyXby
This is how I use json.net to both serialize and deserialize:
public static bool SerializeStudentsFile(string fileStorageLoc)
{
var jsonStudents = JsonConvert.SerializeObject(StudentsList);
System.IO.File.WriteAllText(fileStorageLoc, jsonStudents);
return true;
}
public static List<Student> DeserializeStudentsFile()
{
List<Student> studentList;
if (!System.IO.File.Exists(STUDENTS_FILENAME))
{
var studentFile = System.IO.File.Create(STUDENTS_FILENAME);
studentFile.Close();
}
var studentContentsFile = System.IO.File.ReadAllText(STUDENTS_FILENAME);
var studentContentsFileDeserialized = JsonConvert.DeserializeObject<List<Student>>(studentContentsFile);
if (null != studentContentsFileDeserialized) return studentContentsFileDeserialized;
studentList = new List<Student>();
return studentList;
}