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);
}
}
}
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 have DeserializeObject to a C# object however I have objects with dynamic object names so I have structured it like this:
public class RootObject
{
public string name { get; set; }
public TableLayout table{ get; set; }
}
public class TableLayout
{
public Attributes attributes { get; set; } //Static
public Info info { get; set; } //Static
[JsonExtensionData]
public Dictionary<string, JToken> item { get; set; }
}
So basically any dynamic objects that appear will be added to the dictionary and using JsonExtensionData will populate the rest of the property without creating the object classes. Here is my json:
string json = #"
{
"name": "Table 100",
"table": {
"attributes ": {
"id": "attributes",
"type": "attributes"
},
"info": {
"id": "info",
"type": "info"
},
"item-id12": {
"id": "item-id12",
"type": "Row"
"index": 0
},
"item-id16": {
"id": "item-id16",
"type": "Column"
"parentid": "item-id12"
},
"item-id21": {
"id": "item-id21",
"type": "Column",
"parentid": "item-id12"
}
}
}";
How can I use type ="row" and index value(increments to index 1 to evaluate next row) property to get all columns using parentId of column objects in my Dictionary.
Desired Output:
"item-id12": {
"id": "item-id12",
"type": "Row"
"index": 0
},
"item-id16": {
"id": "item-id16",
"type": "Column"
"parentid": "item-id12"
},
"item-id21": {
"id": "item-id21",
"type": "Column",
"parentid": "item-id12"
}
You can use linq to find your root object
var rootNode = json.table.item.Values
.FirstOrDefault(x => x["type"].Value<string>() == "Row" && x["index"].Value<int>() == 0);
if (rootNode == null)
return; // no such item
Now if this item exists use linq again and get all items from dictionary:
var childNodes = json.table.item.Values
.Where(x => x["parentid"]?.Value<string>() == rootNode["id"].Value<string>());
Next code
var output = new[] {rootNode}.Concat(childNodes);
foreach (var item in output)
Console.WriteLine(item);
will print
{
"id": "item-id12",
"type": "Row",
"index": 0
}
{
"id": "item-id16",
"type": "Column",
"parentid": "item-id12"
}
{
"id": "item-id21",
"type": "Column",
"parentid": "item-id12"
}
P.S. Your input json is invalid, it missing few commas
I have some JSON and would like to remove a node from the JSON but move the nodes inside the removed node to its parent.
Here is the JSON I am starting with:
{
"Response": {
"Outcome": {
"KeyValueOfstringOutcomepQnxSKQu": [
{
"Key": "Icon",
"Value": {
"DataType": "System.String",
"Field": "Icon",
"Value": "O"
}
},
{
"Key": "IconDescription",
"Value": {
"DataType": "System.String",
"Field": "IconDescription",
"Value": "Old"
}
},
{
"Key": "IconLongDescription",
"Value": {
"DataType": "System.String",
"Field": "IconLongDescription",
"Value": "Older"
}
}
]
}
}
}
This is JSON I want to end up with. I need the KeyValueOfstringOutcomepQnxSKQu node removed, but its children must still be present in the JSON:
{
"Response": {
"Outcome": [
{
"Key": "Icon",
"Value": {
"DataType": "System.String",
"Field": "Icon",
"Value": "O"
}
},
{
"Key": "IconDescription",
"Value": {
"DataType": "System.String",
"Field": "IconDescription",
"Value": "Old"
}
},
{
"Key": "IconLongDescription",
"Value": {
"DataType": "System.String",
"Field": "IconLongDescription",
"Value": "Older"
}
}
]
}
}
I have tried to remove the node like this, but that is as far as I got:
JObject rss = JObject.Parse(Convert.ToString(jsonText));
var x = rss.Remove("KeyValueOfstringOutcomepQnxSKQu");
How can I move the children up?
You can do this using SelectToken and Replace:
JObject rss = JObject.Parse(jsonText);
rss.SelectToken("Response.Outcome")
.Replace(rss.SelectToken("Response.Outcome.KeyValueOfstringOutcomepQnxSKQu"));
jsonText = rss.ToString();
Fiddle: https://dotnetfiddle.net/L6XoAf
I'm wondering if someone can elucidate a method to sort a list of objects based on a child object's attribute.
I'm working with the following model:
public class Content
{
public string Id { get; set; }
public List<ContentAttribute> Attributes { get; set; }
}
public class ContentAttribute
{
public string Value { get; set; }
public string Id { get; set; }
public string Name { get; set; }
}
Some sample data:
[
{
"Id": "123",
"Attributes": [
{
"Value": "abc",
"Id": "1a",
"Name": "name1"
},
{
"Value": "ghi",
"Id": "2b",
"Name": "name2"
}
]
},
{
"Id": "456",
"Attributes": [
{
"Value": "abc",
"Id": "1a",
"Name": "name2"
},
{
"Value": "def",
"Id": "2b",
"Name": "name3"
}
]
},
{
"Id": "789",
"Attributes": [
{
"Value": "abc",
"Id": "1a",
"Name": "name1"
},
{
"Value": "def",
"Id": "2b",
"Name": "name2"
}
]
}
]
How can I sort the Content objects by the Value of a specific attribute Name? For example, I would like to sort the above data by the Value of 'name2',
meaning the result would be
[
{"Id" : "456"},
{"Id" : "789"},
{"Id" : "123"}
]
Any help is greatly appreciated. (Using c#).
If Attributes always has an element with name name2 and you want an exception if it doesn't then:
var sorted = contents.OrderBy(c => c.Attributes.First(a => a.Name == "name2").Value).ToList();
Or if name2 could be missing and it's not deal breaker then use FirstOrDefault
var sorted = contents.OrderBy(c => c.Attributes.FirstOrDefault(a => a.Name == "name2")?.Value).ToList();
"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; }
}