My DeserializeObject is listed below.
How can I use Linq (or any other way) to find Value based on Key?
var dictionaryList = JsonConvert.DeserializeObject<dynamic>(response);
{{
"Resources": [
{
"Key": "HeadingCustomerSegments",
"Value": "Customer segments"
},
{
"Key": "Clear all",
"Value": "Clear all"
},
{
"Key": "Third selection of stores the report will be based on",
"Value": "Third selection of stores the report will be based on"
},
{
"Key": "Select the stores to be included in the Dashboard/Opportunity",
"Value": "Select the stores to be included in the Dashboard/Opportunity"
},
}}
In case you want to do it with Linq without a concrete class you could do it this way:
var dictionaryList = (JObject)JsonConvert.DeserializeObject(#"{
""Resources"": [
{
""Key"": ""HeadingCustomerSegments"",
""Value"": ""Customer segments""
},
{
""Key"": ""Clear all"",
""Value"": ""Clear all""
},
{
""Key"": ""Third selection of stores the report will be based on"",
""Value"": ""Third selection of stores the report will be based on""
},
{
""Key"": ""Select the stores to be included in the Dashboard/Opportunity"",
""Value"": ""Select the stores to be included in the Dashboard/Opportunity""
}]
}");
var element = dictionaryList["Resources"]?.FirstOrDefault(x => x["Key"].Value<string>() == "HeadingCustomerSegments");
var value = element != null ? element["Value"]?.Value<string>() : null;
Console.WriteLine(value);
A more proper way, with a concrete class, would be like this:
void Main()
{
var dictionaryList = JsonConvert.DeserializeObject<Response>(#"{
""Resources"": [
{
""Key"": ""HeadingCustomerSegments"",
""Value"": ""Customer segments""
},
{
""Key"": ""Clear all"",
""Value"": ""Clear all""
},
{
""Key"": ""Third selection of stores the report will be based on"",
""Value"": ""Third selection of stores the report will be based on""
},
{
""Key"": ""Select the stores to be included in the Dashboard/Opportunity"",
""Value"": ""Select the stores to be included in the Dashboard/Opportunity""
}]
}");
var value = dictionaryList.Resources.Where(r => r.Key == "HeadingCustomerSegments").Select(r => r.Value).FirstOrDefault();
Console.WriteLine(value);
}
public class Response
{
public List<Resource> Resources { get; set; }
}
public class Resource
{
public string Key { get; set; }
public string Value { get; set; }
}
For response that you've provided you can use:
var listsOfResources = JObject.Parse(response).SelectToken("Resources").ToList();
but you can try also generate dictionary if you want:
var dictionaryResources = JObject.Parse(response).SelectToken("Resources").ToObject<Dictionary<string, string>>();
for getting single item from listsOfResources you can use e.g. ExpandoObject:
var item = JsonConvert.DeserializeObject<ExpandoObject>(listsOfResources[0].ToString());
Related
I have a challenge where I need to take any object and flatten into a key value pair format
This is working really well for simple classes, and even classes where I have other classes within it
Looking at an example,
public class Buyer
{
[JsonProperty("name")]public string Name { get; set; }
[JsonProperty("address")]public string Address { get; set; }
[JsonProperty("lastPurchase")]public Purchase LastPurchase { get; set; }
public Buyer()
{
Name = "Joe Bloggs";
Address = "An adddress somewhere";
AllPurchases = new List<Purchase>()
{
new Purchase() {PurchaseAmount = 100, PurchaseDateTime = Convert.ToDateTime("2017-01-01")},
new Purchase() {PurchaseAmount = 100, PurchaseDateTime = Convert.ToDateTime("2018-01-01")}
};
LastPurchase = new Purchase() {PurchaseAmount = 100, PurchaseDateTime = Convert.ToDateTime("2018-01-01")};
}
[JsonIgnore]
public List<Purchase> AllPurchases { get; set; }
}
public class Purchase
{
public DateTime PurchaseDateTime { get; set; }
public double PurchaseAmount { get; set; }
}
I have the code below which is my current implementation
var buyer = new Buyer();
var json = JsonConvert.SerializeObject(buyer);
var obj = JObject.Parse(json);
var result = obj.Descendants()
.OfType<JProperty>()
.Where(s => s.Value.Type != JTokenType.Object)
.Select(p => new KeyValuePair<string, string>(p.Path,
p.Value.Type == JTokenType.Array || p.Value.Type == JTokenType.Object
? null : p.Value.ToString()));
var serializerSettings = new JsonSerializerSettings
{
Formatting = Formatting.Indented,
ContractResolver = new CamelCasePropertyNamesContractResolver(),
};
var newJson = JsonConvert.SerializeObject(result, serializerSettings);
Console.WriteLine(newJson);
This generates the Json below which is perfect
[
{
"key": "name",
"value": "Joe Bloggs"
},
{
"key": "address",
"value": "An adddress somewhere"
},
{
"key": "lastPurchase.PurchaseDateTime",
"value": "01/01/2018 00:00:00"
},
{
"key": "lastPurchase.PurchaseAmount",
"value": "100"
}
]
Things get tricky when I introduce serialising the list by removing JsonIgnore
Now I get
[
{
"key": "name",
"value": "Joe Bloggs"
},
{
"key": "address",
"value": "An adddress somewhere"
},
{
"key": "lastPurchase.PurchaseDateTime",
"value": "01/01/2018 00:00:00"
},
{
"key": "lastPurchase.PurchaseAmount",
"value": "100"
},
{
"key": "allPurchases",
"value": null
},
{
"key": "allPurchases[0].PurchaseDateTime",
"value": "01/01/2017 00:00:00"
},
{
"key": "allPurchases[0].PurchaseAmount",
"value": "100"
},
{
"key": "allPurchases[1].PurchaseDateTime",
"value": "01/01/2018 00:00:00"
},
{
"key": "allPurchases[1].PurchaseAmount",
"value": "100"
}
]
This has obviously happened because my logic doesnt have anything specific in it for processing lists
How can I change my logic so that AllPurchases is a key value pair collection with the key being allPurchases[0], allPurchases[1] and the value is separate key value collection, which would avoid key names like allPurchases[0].PurchaseAmount etc?
I need to keep the solution generic so that it will flatten any object into this structure
Paul
From what I can see you want the following.
Values and objects not in the array to be transferred to json objects in form of {key:"propertyPath", value:"valueTostring"}
Sub Objects to arrays of of key value pairs.
Arrays to indexed {key:"property[index]", value:"valueTostringOrObjectKeyValueArray"}
The following
var result = GetItmes(obj);
IEnumerable<KeyValuePair<string,object>> GetItmes(in JToken token, string path = "")
{
return token switch
{
JObject jObject => from prop in token.Children<JProperty>()
from child in GetItmes(prop.Value, string.IsNullOrEmpty(path) ? prop.Name : $"{path}.{prop.Name}")
select child,
JArray jArray => from item in jArray.Select((t, i) => (t, i))
select new KeyValuePair<string, object>($"{path}[{item.i}]",GetItmes(item.t)),
JValue jValue => new[] {
new KeyValuePair<string, object>(path, (object)jValue?.ToString())
},
_ => Enumerable.Empty<KeyValuePair<string, object>>(),
};
}
Will create
[
{
"key": "name",
"value": "Joe Bloggs"
},
{
"key": "address",
"value": "An adddress somewhere"
},
{
"key": "lastPurchase.PurchaseDateTime",
"value": "1/1/2018 12:00:00 AM"
},
{
"key": "lastPurchase.PurchaseAmount",
"value": "100"
},
{
"key": "AllPurchases[0]",
"value": [
{
"key": "PurchaseDateTime",
"value": "1/1/2017 12:00:00 AM"
},
{
"key": "PurchaseAmount",
"value": "100"
}
]
},
{
"key": "AllPurchases[1]",
"value": [
{
"key": "PurchaseDateTime",
"value": "1/1/2018 12:00:00 AM"
},
{
"key": "PurchaseAmount",
"value": "100"
}
]
}
]
This code is recursive and unoptimized I am sure there could be a much more efficient way to do this.
For
IEnumerable<KeyValuePair<string, object>> GetItmes(JToken token, string path = "")
{
switch (token)
{
case JObject jObject:
return from prop in token.Children<JProperty>()
from child in GetItmes(prop.Value, string.IsNullOrEmpty(path) ? prop.Name : $"{path}.{prop.Name}")
select child;
case JArray jArray:
return from item in jArray.Select((t, i) => (t, i))
select new KeyValuePair<string, object>($"{path}[{item.i}]", GetItmes(item.t));
case JValue jValue:
return new[] {
new KeyValuePair<string, object>(path, (object)jValue?.ToString())
};
default: return Enumerable.Empty<KeyValuePair<string, object>>();
};
}
I am using JsonForm to produce dynamic forms in my MVC core web application. I am using System.Text.Json.JsonSerializer.Serialize in the following code in my view model to produce a simple one field form. My aim is to store this json in the database eventually and retrieve it from there.
public TestsViewModel GetFormControls1()
{
var myJsonModel = new
{
schema = new
{
client = new
{
type = "object",
title = "Client",
properties = new
{
forename = new
{
type = "string",
title = "Forename",
minLength = 3,
maxLength = 10,
}
}
}
},
form = new List<Object>
{
new {
key = "client.forename",
title = "Forename"
}
},
value = new
{
client = new
{
forename = "",
}
}
};
TestsViewModel homeVm = new TestsViewModel();
homeVm.FormControls = System.Text.Json.JsonSerializer.Serialize(myJsonModel);
return homeVm;
}
Above code works fine and produces following json schema which is then used to create a form.
{
"schema": {
"client": {
"type": "object",
"title": "Client",
"properties": {
"forename": {
"type": "string",
"title": "Forename",
"minLength": 3,
"maxLength": 10
}
}
}
},
"form": [
{
"key": "client.forename",
"title": "Forename"
}
],
"value": {
"client": {
"forename": ""
}
}
}
I now need to produce an enum for my json so that a drop down for selecting a gender can appear in the form. However, I am unable to do that via c# code. Can someone help? I would like my c# code to produce following json (note two entries for gender in schema and form).
{
"schema": {
"client": {
"type": "object",
"title": "Client",
"properties": {
"forename": {
"type": "string",
"title": "Forename",
"minLength": 3,
"maxLength": 10
},
"gender": {
"type": "string",
"title": "Gender",
"enum": [
"male",
"female",
"alien"
]
}
}
}
},
"form": [
{
"key": "client.forename",
"title": "Forename"
},
{
"key": "client.gender",
"titleMap": {
"male": "Dude",
"female": "Dudette",
"alien": "I'm from outer space!"
}
}
],
"value": {
"client": {
"forename": ""
}
}
}
I have tried using following code but enum is a keyword in c# so I get error.
gender = new
{
type = "string",
title = "Gender",
enum = "[male, female, alien]"
}
Also Enum = "[male, female, alien]" produces "Enum": "[male, female, alien]" instead of "enum": [ "male", "female", "alien" ]
I have a gender lookup table which I will be eventually using to produce above enum somehow so any idea regarding that would be helpful as well.
UPDATE
#dbc's comment provides solution to most of my problem. However, I am still struglling with producing titleMap json if I try to map string to an int.
var gender3 = new
{
type = "string",
title = "Gender",
titleMap = new List<string> { new string("1" + ":" + "Male"), new string("2" + ":" + "Female")}
};
Above code produces
{
"type": "string",
"title": "Gender",
"titleMap": [
"1:Male",
"2:Female"
]
}
However, I need both 1 and Male in their own double quotations inside { } instead of [ ] as shown below.
{
"type": "string",
"title": "Gender",
"titleMap": {
"1": "Male",
"2": "Female"
}
}
enum is a keyword in C# so you need to prepend # to enum
gender = new
{
type = "string",
title = "Gender",
#enum = new string[3]{"male", "female", "alien"}
}
From what I understand, you are trying to serialize an array of enum values as their text names into JSON. The simplest solution for that is to add an array or list of enums property to your TestViewModel and use the JsonStringEnumConverter converter to serialize them as strings rather than numbers.
Here's an example of how you could achieve what you are looking for.
Let's say you have some enum with a set of values:
//This is an arbitrary enum, this could be 'Gender', in your case
public enum TestEnum
{
value1,
value2,
value3,
value4,
}
And you want to write down an array of those enum values. Add a list of enums property (or array of enums) to your model. If you want the property name within the JSON to be one of the reserved words (like enum) either override the name using the JsonPropertyName attribute (and keep the name whatever makes most sense programmatically):
public class TestsViewModel_Option1
{
// In your case, this property could be called 'Genders' to be self-documenting
[JsonPropertyName("enum")]
public List<TestEnum> ListOfEnums { get; set; }
}
OR, use the # symbol if you really want the property name to be the reserved keyword and don't want to/can't use the attribute for some reason.
public class TestsViewModel_Option2
{
// Or use fixed-size array, TestEnum[], if needed
public List<TestEnum> #enum { get; set; }
}
And now this is what your code looks like with the JsonSerializer:
private static void SerializeListOfEnums()
{
var model1 = new TestsViewModel_Option1
{
ListOfEnums = { TestEnum.value1, TestEnum.value3 }
};
var options = new JsonSerializerOptions
{
Converters = { new JsonStringEnumConverter() }
};
// {"enum":["value1","value3"]}
Console.WriteLine(JsonSerializer.Serialize(model1, options));
var model2 = new TestsViewModel_Option2
{
#enum = { TestEnum.value1, TestEnum.value3 }
};
// {"enum":["value1","value3"]}
Console.WriteLine(JsonSerializer.Serialize(model2, options));
}
"fields": [
{
"field": {
"name": "SMS",
"value": "Yes"
}
},
{
"field": {
"name": "Email",
"value": ""
}
},
{
"field": {
"name": "Total",
"value": ""
}
},
]
I have tried to form the JSON format like above, so i formed the class like below. While serialization it does not return expected form, how can i achieve this one.
public class Test
{
public List<Field> fields;
}
public class Field
{
public string name { get; set; }
public string value { get; set; }
}
Response:
"fields": [{
"name": "SMS",
"value": "Yes"
}, {
"name": "Email",
"value": ""
},{
"name": "Total",
"value": ""
}]
Use this website http://json2csharp.com and generate all the classes automatically. Just copy-paste your json there.
You can customize resulting JSON object with anonymous types and LINQ. Please try this code:
var test = new Test {fields = new List<Field>()};
test.fields.Add(new Field {name = "f1", value = "v1"});
test.fields.Add(new Field {name = "f2", value = "v2"});
var json = JObject.FromObject(new { fields = test.fields.Select(f => new {field = f}).ToArray() })
.ToString();
A json variable would be:
{
"fields": [
{
"field": {
"name": "f1",
"value": "v1"
}
},
{
"field": {
"name": "f2",
"value": "v2"
}
}
]
}
You just missed a class level:
public class Test
{
public List<FieldHolder> fields;
}
public class FieldHolder
{
public Field field { get; set; }
}
public class Field
{
public string name { get; set; }
public string value { get; set; }
}
I receive the following JSON string. It is part of the specifications of products. The data represents the Header ("General") and the items within in Key value pairs.
I dont want to hard code any properties such as general, Display Features and should be able to
Retrieve the Header
Iterate on the key value pair.
So Far I have the following code
foreach (var SpecList in objAPIProduct.categorySpecificInfoV1.specificationList)
{
foreach(dynamic SpecItem in SpecList.General)
{
Console.WriteLine(SpecItem.key);
Console.WriteLine(SpecItem.value[0]);
}
}
I want to get rid of the "General" Property that I have hardcoded in the inner foreach loop and make the loop generic for all properties.
Here is the Json that I have
{
"General": [{
"key": "Sales Package",
"value": ["Laptop, Power Adapter, Warranty Document"]
},
{
"key": "Model Number",
"value": ["3558"]
},
{
"key": "Part Number",
"value": ["Z565103UIN9"]
},
{
"key": "Model Name",
"value": ["Inspiron 15"]
},
{
"key": "Series",
"value": ["Inspiron"]
},
{
"key": "Color",
"value": ["Black"]
},
{
"key": "Type",
"value": ["Notebook"]
},
{
"key": "Suitable For",
"value": ["Everyday Use"]
},
{
"key": "Battery Backup",
"value": ["Upto 3.5 hours"]
},
{
"key": "Battery Cell",
"value": ["4 cell"]
}]
},
{
"Processor and Memory Features": [{
"key": "Processor Brand",
"value": ["Intel"]
},
{
"key": "Processor Name",
"value": ["Core i3"]
},
{
"key": "Graphic Processor",
"value": ["Intel HD Graphics 5500"]
},
{
"key": "SSD",
"value": ["No"]
},
{
"key": "RAM",
"value": ["4 GB"]
},
{
"key": "RAM Type",
"value": ["DDR3"]
},
{
"key": "HDD Capacity",
"value": ["500 GB"]
},
{
"key": "Processor Variant",
"value": ["5005U"]
},
{
"key": "Clock Speed",
"value": ["2 GHz"]
},
{
"key": "Memory Slots",
"value": ["1 Slot"]
},
{
"key": "Expandable Memory",
"value": ["Upto 4 GB"]
},
{
"key": "RAM Frequency",
"value": ["1600 MHz"]
},
{
"key": "Cache",
"value": ["3 MB"]
},
{
"key": "RPM",
"value": ["5400"]
}]
}
On site json2csharp.com you can generate classes for your JSON.
public class General
{
public string key { get; set; }
public List<string> value { get; set; }
}
public class RootObject
{
public List<General> General { get; set; }
}
And deserialize JSON to it using JSON.NET:
RootObject ro = JsonConvert.DeserializeObject<RootObject>(json);
And:
foreach(General g in ro.General)
{
string k = g.key;
}
Rewrite your SpecList class like this
public class SpecList : Dictionary<string, List<SpecItem>>
{
}
From then you will be able to access your general property like this
foreach (var SpecList in objAPIProduct.categorySpecificInfoV1.specificationList)
{
foreach(var key in SpecList.Keys)
{
foreach(var SpecItem in SpecList[key])
{
Console.WriteLine(SpecItem.key);
Console.WriteLine(SpecItem.value[0]);
}
}
}
Hope it helps.
After trying out a lot of options, I found that it is not possible to get the "Name" of the property when we use Json.Net or JavascriptSerialiser.
Finally, I used the help of this thread -
Deserialize JSON into C# dynamic object?
wherein the person has suggested using System.Web.Helpers and it works perfect in my case.
Here is my final code:
dynamic objAPIProduct= Json.Decode(json);
List<ProductSpecification> objProductSpecificationList = new List<ProductSpecification>();
ProductSpecification objProductSpecification;
foreach (var SpecListCategoryList in objAPIProduct.categorySpecificInfoV1.specificationList)
{
foreach (var SpecCategory in SpecListCategoryList)
{
DynamicJsonArray objListitems = SpecCategory.Value;
for (int i = 0; i < objListitems.Length; i++)
{
objProductSpecification = new ProductSpecification();
objProductSpecification.SpecificationHead = SpecCategory.Key;
objProductSpecification.SpecificationKey = objListitems[i].key;
objProductSpecification.SpecificationValue = objListitems[i].value[0];
objProductSpecificationList.Add(objProductSpecification);
}
}
}
My approximate data table structure which i will get from my sqlserver stored proc call. I am using .netframework 3.5 and i want to convert this datatable to json output using ado.net. i have stuck at GetCountryList(FK,TypeName). Kindly help me to acheive the below json output. Thanks for your help in advance.
Type ID Name FK
Continent 1 America 0
Continent 2 Asia 0
Continent 3 Africa 0
Country 11 USA 1
Country 12 China 2
Country 13 India 2
Country 14 Kenya 3
DataEntity
public class UserData
{
public string Type { get; set; }
public string ID { get; set; }
public string Name { get; set; }
public string FK {get; set;}
}
Service Method
public static UserData GetUserData() {
JavaScriptSerializer jSerializer = new JavaScriptSerializer ();
DataTable dtUserData = DataAccess.getUserDataTable();
if(dtUserData !=null && dtUserData.Rows.Count>0)
{
List<DataRow> list = dtMasterData.AsEnumerable().ToList();
List<UserData> lstContinent = new List<UserData>();
List<UserData> lstCountry = new List<UserData>();
foreach(DataRow dr in list)
{
var objUserData = new UserData();
objUserData.ID = dr["ID"].ToString();
objUserData.Type = dr["Type"].ToString();
objUserData.Name = dr["Name"].ToString();
objUserData.FK = dr["FK"].ToString();
if(objUserData.Type.ToString().ToLower=="continent")
{
lstContinent.Add(objUserData);
}
if(objUserData.Type.ToString().ToLower=="country")
{
if(dr["FK"] !=null)
{
var ForgnKey = dr["FK"].ToString();
var TypeName = dr["Type"].ToString();
var CountriesList = GetCountryList(FK,TypeName) //how do i call a generic method to filter out the country list as per passing Continent FK?
lstCountry.AddRange(CountriesList);
}
lstCountry.Add(objUserData);
}
private static List<T> GetCountryList (lstCountry,ForgnKey,TypeName) //Not sure with the syntax
{
var CountriesList = lstCountry.Where(p=>p.FK==ForgnKey).ToList();
}
}
return jSerializer.Serialize(objUserData);
}
Expected JSON Output :
"data": {
"Contnient":
[
{ "Id": "1", "Type": "Contient", "Name" :"America","FK":"1" },
{ "Id": "2", "Type": "Contient", "Name" :"Asia", "FK":"2" },
{ "Id": "3", "Type": "Contient", "Name" :"Africa", "FK":"2" },
{ "Id": "4", "Type": "Contient", "Name" :"Asia", "FK":"2" }
],
"America":
{
"Country":
[
{ "Id": "11", "Type": "Country","Name":"India","FK":"1" }
]
},
"Asia:
{
"Country":
[ { "Id": "12", "Type": "Country","Name":"China","FK":"2" },
{ "Id": "13", "Type": "Country","Name":"India","FK":"2" }
]
}
"Africa":
{
"Country":
[ { "Id": "14", "Type": "Country","Name":"Kenya","FK":"3" }
]
}
Shouldn't the generic function be defined as:
private static List<T> GetCountryList<T>(lstCountry,ForgnKey,TypeName)
{
return lstCountry.Where(p=>p.FK==ForgnKey).ToList();
}
Then called as:
var CountriesList = GetCountryList<UserData>(lstCountry,FK,TypeName);