Generically Flatten JSON Using C#

Generically Flatten Json using c#

You can use Json.Net's LINQ-to-JSON API to parse the data into a JToken structure. From there, you can use a recursive helper method to walk the structure and flatten it to a Dictionary<string, object> where the keys are the "path" to each value from the original JSON. I would write it something like this:

public class JsonHelper
{
public static Dictionary<string, object> DeserializeAndFlatten(string json)
{
Dictionary<string, object> dict = new Dictionary<string, object>();
JToken token = JToken.Parse(json);
FillDictionaryFromJToken(dict, token, "");
return dict;
}

private static void FillDictionaryFromJToken(Dictionary<string, object> dict, JToken token, string prefix)
{
switch (token.Type)
{
case JTokenType.Object:
foreach (JProperty prop in token.Children<JProperty>())
{
FillDictionaryFromJToken(dict, prop.Value, Join(prefix, prop.Name));
}
break;

case JTokenType.Array:
int index = 0;
foreach (JToken value in token.Children())
{
FillDictionaryFromJToken(dict, value, Join(prefix, index.ToString()));
index++;
}
break;

default:
dict.Add(prefix, ((JValue)token).Value);
break;
}
}

private static string Join(string prefix, string name)
{
return (string.IsNullOrEmpty(prefix) ? name : prefix + "." + name);
}
}

Using this DeserializeAndFlatten method with your JSON you would end up with key-value pairs like this:

appointmentid: 4
policyid: 1
guid: 00000000-0000-0000-0000-000000000000
number: 1234567890
ampm: false
date: 9/8/2015 12:00:00 AM
vehicle.id: 1
vehicle.guid: 00000000-0000-0000-0000-000000000000
vehicle.make:
vehicle.model:
installer.installerid: 1
installer.name: Installer 1
installer.contact: qwerty
installer.qascore: 0
installer.address1: qwerty
installer.address2: qwerty
installer.address3:
installer.address4:
installer.city: qwertyu
installer.county: qwertyu
installer.postcode: asdfghj
installer.country: GB
installer.email: asdfghj
installer.web: asdfghjk
installer.archived: False
installations.0.installationid: 6
installations.0.installationstatus.installationstatusid: 4
installations.0.installationstatus.installationstatus: FAIL
installations.0.isactive: True
installations.1.installationid: 7
installations.1.installationstatus.installationstatusid: 1
installations.1.installationstatus.installationstatus: NEW
installations.1.isactive: False
archived: False

If you're looking to make the keys more human friendly, you could use a little string manipulation to cut them down. Maybe something like this:

var dict = JsonHelper.DeserializeAndFlatten(json);
foreach (var kvp in dict)
{
int i = kvp.Key.LastIndexOf(".");
string key = (i > -1 ? kvp.Key.Substring(i + 1) : kvp.Key);
Match m = Regex.Match(kvp.Key, @"\.([0-9]+)\.");
if (m.Success) key += m.Groups[1].Value;
Console.WriteLine(key + ": " + kvp.Value);
}

That would give you this output instead:

appointmentid: 4
policyid: 1
guid: 00000000-0000-0000-0000-000000000000
number: 1234567890
ampm: false
date: 9/8/2015 12:00:00 AM
id: 1
guid: 00000000-0000-0000-0000-000000000000
make:
model:
installerid: 1
name: Installer 1
contact: qwerty
qascore: 0
address1: qwerty
address2: qwerty
address3:
address4:
city: qwertyu
county: qwertyu
postcode: asdfghj
country: GB
email: asdfghj
web: asdfghjk
archived: False
installationid0: 6
installationstatusid0: 4
installationstatus0: FAIL
isactive0: True
installationid1: 7
installationstatusid1: 1
installationstatus1: NEW
isactive1: False
archived: False

But note, with this arrangement, you have lost some context: for example, you can see that there are now two identical archived keys, whereas in the original JSON they were distinct because they appeared in different parts of the hierarchy (installer.archived vs. archived). You will need to figure out how to deal with that problem on your own.

Fiddle: https://dotnetfiddle.net/gzhWHk

flattening json structure with arrays to multiple flat objects without arrays

You could do it with a function like this:

//Pass in the name of the array property you want to flatten
public string FlattenJson(string input, string arrayProperty)
{
//Convert it to a JObject
var unflattened = JsonConvert.DeserializeObject<JObject>(input);

//Return a new array of items made up of the inner properties
//of the array and the outer properties
var flattened = ((JArray)unflattened[arrayProperty])
.Select(item => new JObject(
unflattened.Properties().Where(p => p.Name != arrayProperty),
((JObject)item).Properties()));

//Convert it back to Json
return JsonConvert.SerializeObject(flattened);
}

And call it like this:

var flattenedJson = FlattenJson(inputJson, "lineItems");

How can I flatten my data model's JSON output in C#?

You just need to create a flattened dictionary. You can achieve this by manually adding your keys and values for static properties, and then dynamically adding your existing key value pairs in the Address dictionary.

Personally, I recommend creating a method that flattens out the data and gives it back as a JSON string:

public class Person {
public string Name { get; set; }
public DateTime DOB { get; set; }
public Dictionary<string, string> Address { get; set; }
public string GetFlattenedJson() {
var result = new Dictionary<string, object> {
{ "Name", Name },
{ "DOB", DOB }
};
foreach (var datum in Address)
result.Add(datum.Key, datum.Value);

return JsonConvert.SerializeObject(result);
}
}

Then, when you need the flattened data, you simply access it with that method:

var flatJson = myPerson.GetFlattenedJson();

How to deserialize JSON with child attribute into ONE object in C#

Just nest one object inside the other. (If you're using EF, maybe it would be better to have 2 separate Events: one for your DbSet (without nested object) and another as a JSON data contract.)

public class Event
{
//.. the rest of your attributes here
public EventParameters Parameters { get; set; }

}

public class EventParameters
{
public int? Age { get; set; } // If such parameter may not exist in your JSON, ensure it to be nullable in your POCO.
public string Country { get; set; }
public string Gender { get; set; }
}

Convert JSON to DataTable

Deserialize your jsonstring to some class

List<User> UserList = JsonConvert.DeserializeObject<List<User>>(jsonString);

Write following extension method to your project

using System.ComponentModel;

public static DataTable ToDataTable<T>(this IList<T> data)
{
PropertyDescriptorCollection props =
TypeDescriptor.GetProperties(typeof(T));
DataTable table = new DataTable();
for(int i = 0 ; i < props.Count ; i++)
{
PropertyDescriptor prop = props[i];
table.Columns.Add(prop.Name, prop.PropertyType);
}
object[] values = new object[props.Count];
foreach (T item in data)
{
for (int i = 0; i < values.Length; i++)
{
values[i] = props[i].GetValue(item);
}
table.Rows.Add(values);
}
return table;
}

Call extension method like

UserList.ToDataTable<User>();

Is there any way to dynamically create properties from Json string in Winforms using C#?

You can implement Visitor pattern (something similar to ExpressionVisitor):

public class JsonVisitor
{
private readonly HashSet<string> uniqueProperties;
private readonly List<string> allProperties;

public JsonVisitor()
{
uniqueProperties = new HashSet<string>();
allProperties = new List<string>();
}

public string UniqueProperties => string.Join(Environment.NewLine, uniqueProperties);
public string AllProperties => string.Join(Environment.NewLine, allProperties);

public void Visit(JObject jObject)
{
uniqueProperties.Clear();
allProperties.Clear();

VisitObject(jObject, "");
}

private void VisitObject(JObject jObject, string currentPath)
{
foreach (var property in jObject.Properties())
{
uniqueProperties.Add(property.Name);
var path = $"{currentPath}/{property.Name}";
allProperties.Add(path);

VisitToken(property.Value, path);
}
}

private void VisitArray(JArray jArray, string currentPath)
{
for (var i = 0; i < jArray.Count; ++i)
{
var item = jArray[i];
var path = $"{currentPath}[{i}]";
VisitToken(item, path);
}
}

private void VisitToken(JToken token, string currentPath)
{
if (token is JObject childObject)
VisitObject(childObject, currentPath);
else if (token is JArray childArray)
VisitArray(childArray, currentPath);

}
}

Usage:

var input = "{\r\n  \"json\":{\r\n    \"CompanyId\":null,\r\n    \"Name\":null,\r\n    \"UniqueIdentity\":null,\r\n    \"AccountCode\":null,\r\n    \"IsActive\":null,\r\n    \"ContractNumber\":null,\r\n    \"IsSiteCompany\":null,\r\n    \"TaxCode\":null,\r\n    \"GLCode\":null,\r\n    \"CompanyTypeId\":null,\r\n    \"AddressInfo\":[\r\n      {\r\n        \"AddressId\":null,\r\n        \"CompanyId\":null,\r\n        \"AddressTypeId\":null,\r\n        \"Phone\":null,\r\n        \"Fax\":null,\r\n        \"Address\":null,\r\n        \"Address2\":null,\r\n        \"City\":null,\r\n        \"State\":null,\r\n        \"Zip\":null,\r\n        \"Country\":null\r\n      }\r\n    ],\r\n    \"ContactInfo\":[\r\n      {\r\n        \"ContactId\":null,\r\n        \"CompanyId\":null,\r\n        \"ContactTypeId\":null,\r\n        \"FirstName\":null,\r\n        \"MiddleName\":null,\r\n        \"LastName\":null,\r\n        \"Alias\":null,\r\n        \"Position\":null,\r\n        \"Email\":null,\r\n        \"Phone\":null,\r\n        \"IsPrimaryContact\":null\r\n      }\r\n    ]\r\n  }\r\n}";
var json = JObject.Parse(input);

var visitor = new JsonVisitor();
visitor.Visit(json);

Console.WriteLine($"Unique properties:\r\n{visitor.UniqueProperties}");
Console.WriteLine();
Console.WriteLine($"All properties:\r\n{visitor.AllProperties}");

Output:

Unique properties:
json
CompanyId
Name
UniqueIdentity
AccountCode
// and so on

All properties:
/json
/json/CompanyId
/json/Name
/json/UniqueIdentity
/json/AccountCode
/json/IsActive
/json/ContractNumber
/json/IsSiteCompany
/json/TaxCode
/json/GLCode
/json/CompanyTypeId
/json/AddressInfo
/json/AddressInfo[0]/AddressId
/json/AddressInfo[0]/CompanyId
/json/AddressInfo[0]/AddressTypeId
/json/AddressInfo[0]/Phone
/json/AddressInfo[0]/Fax
// and so on


Related Topics



Leave a reply



Submit