Deserializing Json to Obj with two same variables in Json - c#

Does someone know how to handle JSON string deserialize in C#, which has values string&List simultaneously?
Example:
{
"data": {
"productStream": {
"edges": [{
"node": {
"attributeList": {
"edges": [{
"node": {
"selectAttributeValueTranslations": [{
"value": {
"name": [{
"value": "test"
},
{
"value": null
}
]
}
}]
}
},
{
"node": {
"attribute": {
"code": "example"
},
"imageAttributeValueTranslations": [{
"value": {
"name": "test.jpg",
"extension": "jpg"
}
}]
}
}
]
}
}
}]
}
}
}
And when I'm creating an object to deserialize it I have no idea how to declare this name.
public List Name doesn't work for "name" = "example.jpg". Otherwise public string Name doesn't work for "name"[ value = "example"]

I had to add another class based on JSON Data which contains a value for all attributes and the second class contains a value for only one attribute.

Related

Filtering Nested array with linq

I have an array like this coming from API response and I want to filter by nested property products to returns product with only id 11
"assets": [
{
"name": "abc",
"products": [
{
"id": "11",
"status": true
},
{
"id": "14",
"status": null
}
]
},
{
"name": "xyz",
"products": [
{
"id": "11",
"status": true
},
{
"id": "28",
"status": null
}
]
},
{
"name": "123",
"products": [
{
"id": "48",
"status": null
}
]
},
{
"name": "456",
"products": [
{
"id": "11",
"status": true
}
]
}
]
the resulting output should look like this,
"assets": [
{
"name": "abc",
"products": [
{
"id": "11",
"status": true
}
]
},
{
"name": "xyz",
"products": [
{
"id": "11",
"status": true
}
]
},
{
"name": "123",
"products": []
},
{
"name": "456",
"products": [
{
"id": "11",
"status": true
}
]
}
]
I'm trying to return filter output with the C# LINQ method. but, getting an error when filtering a nested array with the following method
"Cannot convert lambda expression to intended delegate type because some of the return types in the block are not implicitly convertible to the delegate return type"
assets.Select(asset => asset.products.Where(product => product.id == "11")).ToArray();
You can just remove all items from every products array, whose id doesn't equal 11. Parse JSON to JObject, iterate over assets array, find products with id different from 11 and remove them form original array
var json = JObject.Parse(jsonString);
foreach (var asset in json["assets"])
{
var productsArray = asset["products"] as JArray;
if (productsArray == null)
continue;
var productsToRemove = productsArray
.Where(o => o?["id"]?.Value<int>() != 11)
.ToList();
foreach (var product in productsToRemove)
productsArray.Remove(product);
}
Console.WriteLine(json);
Instead of Select and Applying a, Where try to remove the Products that you do not want.
Following is a sample code.
class Program
{
static void Main(string[] args)
{
var json = #"
{
'assets': [
{
'name': 'abc',
'products': [
{
'id': '11',
'status': true
},
{
'id': '14',
'status': null
}
]
},
{
'name': 'xyz',
'products': [
{
'id': '11',
'status': true
},
{
'id': '28',
'status': null
}
]
},
{
'name': '123',
'products': [
{
'id': '48',
'status': null
}
]
},
{
'name': '456',
'products': [
{
'id': '11',
'status': true
}
]
}
]}";
var root = JsonConvert.DeserializeObject<Root>(json);
var assets = root.Assets;
assets.ForEach(a =>
{
a.Products.RemoveAll(p => p.Id != 11);
});
}
}
public partial class Root
{
[JsonProperty("assets")]
public List<Asset> Assets { get; set; }
}
public partial class Asset
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("products")]
public List<Product> Products { get; set; }
}
public partial class Product
{
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("status")]
public bool? Status { get; set; }
}
To filter the list, you will need to create a new list with products whose ID == YourId. Following linq query does the necessary steps to create the list you want.
Filter out any assets that dont have any product with ID == 11. This is necessary to skip nulls in the new list that will be generated (Where statement)
Create a collection / list of new assets that only have the products whose ID == 11 (Select statement)
var list = assets.Where(x => x.Products.Where(y => y.Id.Equals("11")).Count() > 0)
.Select(asset => {
return new Asset() {
Name = asset.Name,
Products = asset.Products.Where(x => x.Id == "11").ToList()
};
}).ToList();
// Create the RootObject that holds a list of all the arrays if you want.
Rootobject newAssetCollection = new Rootobject() { Assets = list };
Below is the Json that was printed
Console.WriteLine(JsonConvert.SerializeObject(newAssetCollection, Formatting.Indented));
{
"assets": [
{
"name": "abc",
"products": [
{
"id": "11",
"status": true
}
]
},
{
"name": "xyz",
"products": [
{
"id": "11",
"status": true
}
]
},
{
"name": "456",
"products": [
{
"id": "11",
"status": true
}
]
}
]
}

Get nested fieldNames from JSON object

I have a JSON that contains a list of types. These types have some name and some fields.
Fields have a property of dataType and if it's value is object, it refers to a another type. That type could be found by its name specified in referenceType property.
There is also a property named parentType which means the type is a child of the parentType and it contains some additional properties, but have to be considered as an object of parentType only.
I am trying to process this JSON to get all the nested property names for all the types present in the array.
{
"types": [
{
"name": "User1",
"fields": [
{
"name": "name",
"dataType": "string"
},
{
"name": "address",
"dataType": "object",
"referenceType": "Address",
"isArray": true
},
{
"name": "weeklyRoles",
"dataType": "object",
"isArray": true,
"referenceType": "Role"
}
]
},
{
"name": "User2",
"fields": [
{
"name": "name",
"dataType": "string"
},
{
"name": "address",
"dataType": "object",
"referenceType": "Address",
"isArray": true
}
]
},
{
"name": "Address",
"fields": [
{
"name": "AddressLine1",
"dataType": "string"
},
{
"name": "AddressLine2",
"dataType": "string"
}
]
},
{
"name": "BusinessAddress",
"parentType": "Address",
"fields": [
{
"name": "headquarters",
"dataType": "string"
}
]
},
{
"name": "ServiceAddress",
"parentType": "Address",
"fields": [
{
"name": "servicePartner",
"dataType": "string"
},
{
"name": "serviceType",
"dataType": "string"
}
]
},
{
"name": "Role",
"fields": [
{
"name": "roleName",
"dataType": "string"
},
{
"name": "accessCountsObj1",
"dataType": "object",
"referenceType": "Role2"
}
]
},
{
"name": "Role2",
"fields": [
{
"name": "roleName2",
"dataType": "string"
},
{
"name": "accessCountsObj2",
"dataType": "object",
"referenceType": "Role3"
}
]
},
{
"name": "Role3",
"fields": [
{
"name": "roleName3",
"dataType": "string"
},
{
"name": "accessCountsObj3",
"dataType": "object",
"referenceType": "Role4"
}
]
},
{
"name": "Role4",
"fields": [
{
"name": "roleName4",
"dataType": "string"
}
]
}
]
}
Pattern in which I am expecting the result is <typeName>;<fieldName>.<nestedFieldName>
Expected Output
[
"User1;address.AddressLine1",
"User1;address.AddressLine2",
"User1;address.headquarters",
"User1;address.servicePartner",
"User1;address.serviceType",
"User1;weeklyRoles.roleName",
"User1;weeklyRoles.accessCountsObj1.roleName2",
"User1;weeklyRoles.accessCountsObj1.accessCountsObj2.roleName3",
"User1;weeklyRoles.accessCountsObj1.accessCountsObj2.accessCountsObj3.roleName4",
"User2;address.AddressLine1",
"User2;address.AddressLine2",
"User2;address.headquarters",
"User2;address.servicePartner",
"User2;address.serviceType",
"Role;accessCountsObj1.roleName2",
"Role;accessCountsObj1.accessCountsObj2.roleName3",
"Role;accessCountsObj1.accessCountsObj2.accessCountsObj3.roleName4",
"Role2;accessCountsObj2.roleName3",
"Role2;accessCountsObj2.accessCountsObj3.roleName4",
]
I have tried to write a recursive function to process it but it is not giving me the expected result and also does not have terminating condition.
public IList<string> GetKeys(JArray types, string parentType = null)
{
var nestedKeys = new List<string>();
foreach (var type in types)
{
var fields = type[Constants.Fields].ToObject<List<JObject>>();
var typeName = type.Value<string>("name");
var nestedKeyBuilder = new StringBuilder($"{typeName};");
if (!string.IsNullOrEmpty(parentType))
{
nestedKeyBuilder = new StringBuilder($"{parentType};");
}
foreach (var field in fields)
{
var datatype = field.Value<string>("dataType");
if (string.Equals(datatype,"object"))
{
var fieldName = field.Value<string>("name");
var referenceTypeName = field.Value<string>("referenceType");
var referenceTypeObject = types.Where(t => string.Equals(t.Value<string>("name"), referenceTypeName))?.First();
if (referenceTypeObject != null)
{
var refTypeFields = referenceTypeObject["fields"].ToObject<List<JObject>>();
foreach (var refTypeField in refTypeFields)
{
var refTypeFieldName = refTypeField.Value<string>("name");
var refTypeDataType = refTypeField.Value<string>("dataType");
var refTypeReferenceTypeName = refTypeField.Value<string>("referenceType");
if (string.Equals(refTypeDataType, "object") && string.Equals(refTypeReferenceTypeName, currentReferenceType))
{
var refTypeNestedKeys = GetKeys(types, typeName);
nestedKeys.AddRange(refTypeNestedKeys);
}
else
{
nestedKeyBuilder.Append($"{fieldName}.{refTypeFieldName}");
nestedKeys.Add(nestedKeyBuilder.ToString());
}
}
}
}
}
}
return nestedKeys;
}
There is a NuGet that you can use to handle JSON files.
Search for Newtonsoft.Json
https://www.newtonsoft.com/json
All your problems should be solved with that
You can use Newtonsoft library to do this.
This is your POCO class.
{
public string name { get; set; }
public string dataType { get; set; }
public string referenceType { get; set; }
public bool? isArray { get; set; }
}
public class Type
{
public string name { get; set; }
public List<Field> fields { get; set; }
public string parentType { get; set; }
}
public class RootObject
{
public List<Type> types { get; set; }
}
and write the function to DeserializeObject
[TestMethod]
public void Read()
{
var sample1 = #"X:\JsonFilePath\data.json";
var jsonString=File.ReadAllText(sample1);
var result =JsonConvert.DeserializeObject<RootObject>(jsonString);
Assert.IsNotNull(result);
}
Use newtonsoft JSON to Deseriliaze the JSON string.
JSON.Deserialize<IEnumerable<Type>>(jsonString);
This should give you an IEnumerable to process it however you want. Types is the class you create with the JSON properties as class properties. You can use something like this
NOTE: Type and Field are very vague names. Type is a c# class and therefore you should not use it to create a class of your own. You will face ambiguous errors and you will need to qualify the namespace in full to use your class. Strongly suggesting to name it something else and use the attributes to map it to the JSON string.
[JsonObject(Title="People")]
public class Type
{
[JsonProperty("name")]
string Name{ get; set; }
[JsonProperty("fields")]
Field FieldValue[]{ get; set; }
}

Dictionary<string, JToken> recursive search

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

Json Path querying without an array

In the below JSON I am trying to find a condition.Where I want the result only if OrderNumber is not empty or null.
Conditions I have tried. But havent worked for me are:
> $..Item[?(#.OrderNumber)]
> $..Item[?(#.CurrentOrder.OrderNumber)]
Any suggestions will be appreciated.
I am testing my queries here https://jsonpath.curiousconcept.com/
{
"Response": {
"ID": "123456",
"Items": {
"Item": [
{
"CurrentOrder": {
"OrderNumber": "123",
"Status": ""
}
}
]
}
}
}
$..Item[?(#.CurrentOrder.OrderNumber)] is the correct JSONPath for your case.
The tool you used is known to not working correctly. See for example this question.
I have test above query with JSONPath Online Evaluator and for JSON
{
"Response": {
"ID": "123456",
"Items": {
"Item": [
{
"CurrentOrder": {
"OrderNumber": "123",
"Status": ""
}
},
{
"CurrentOrder": {
"OrderNumber": "456",
"Status": ""
}
},
{
"CurrentOrder": {
"Status": ""
}
}
]
}
}
}
it gives correct result:
[
{
"CurrentOrder": {
"OrderNumber": "123",
"Status": ""
}
},
{
"CurrentOrder": {
"OrderNumber": "456",
"Status": ""
}
}
]

JSON single property formatting

I have following JSON:
{
"cap": [
{
"type": "test"
},
{
"type": "test1"
},
{
"type": "test2",
"bla": "tst"
}
]
}
I want every object that have single property to be one line formated
{ "type": "test" }
so the final JSON should be
{
"cap": [
{ "type": "test" },
{ "type": "test1" },
{
"type": "test2",
"bla": "tst"
}
]
}
How to do this in c#?

Categories