from this Answer I learned how to flatten a JSON object in c#. from JSON String:
{"menu": {
"id": "file",
"value": "File",
"popup": {
"menuitem": [
{"value": "New", "onclick": "CreateNewDoc()"},
{"value": "Open", "onclick": "OpenDoc()"},
{"value": "Close", "onclick": "CloseDoc()"}
]
}
}}
To:
The following are lines of strings, not an object
menu.id:file
menu.value:File
menu.popup.menuitem[0].value:New
menu.popup.menuitem[0].onclick:CreateNewDoc()
menu.popup.menuitem[1].value:Open
menu.popup.menuitem[1].onclick:OpenDoc()
menu.popup.menuitem[2].value:Close
menu.popup.menuitem[2].onclick:CloseDoc()
Now, i want to reverse the process. I can found implementations from this question but it is in JavaScript.
How do I unflatten (return structured JSON from lines) it in C# with json.net?
We can unflatten a flattened JSON object by splitting property paths and then adding them into the unflattened object with a loop.
Flatten a JSON object: var flatten = (function (isArray, wrapped) { return function (table) { return reduce("", {}, table); }; function reduce(path, accumulator, table) { if (isArray(table)) { var length = table.
Use the Flatten JSON Objects extension to convert a nested data layer object into a new object with only one layer of key/value pairs. For more advanced options in converting a data layer, see the Data Layer Converter.
I managed to solve it out.
Below is my code combined with Sarath Rachuri's flattening code.
I did not test it in too many cases, so it could be buggy.
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
namespace JSONHelper
{
class JSONFlattener
{
private enum JSONType{
OBJECT, ARRAY
}
public static Dictionary<string, string> Flatten(JObject jsonObject)
{
IEnumerable<JToken> jTokens = jsonObject.Descendants().Where(p => p.Count() == 0);
Dictionary<string, string> results = jTokens.Aggregate(new Dictionary<string, string>(), (properties, jToken) =>
{
properties.Add(jToken.Path, jToken.ToString());
return properties;
});
return results;
}
public static JObject Unflatten(IDictionary<string, string> keyValues)
{
JContainer result = null;
JsonMergeSettings setting = new JsonMergeSettings();
setting.MergeArrayHandling = MergeArrayHandling.Merge;
foreach (var pathValue in keyValues)
{
if (result == null)
{
result = UnflatenSingle(pathValue);
}
else
{
result.Merge(UnflatenSingle(pathValue), setting);
}
}
return result as JObject;
}
private static JContainer UnflatenSingle(KeyValuePair<string, string> keyValue)
{
string path = keyValue.Key;
string value = keyValue.Value;
var pathSegments = SplitPath(path);
JContainer lastItem = null;
//build from leaf to root
foreach (var pathSegment in pathSegments.Reverse())
{
var type = GetJSONType(pathSegment);
switch (type)
{
case JSONType.OBJECT:
var obj = new JObject();
if (null == lastItem)
{
obj.Add(pathSegment,value);
}
else
{
obj.Add(pathSegment,lastItem);
}
lastItem = obj;
break;
case JSONType.ARRAY:
var array = new JArray();
int index = GetArrayIndex(pathSegment);
array = FillEmpty(array, index);
if (lastItem == null)
{
array[index] = value;
}
else
{
array[index] = lastItem;
}
lastItem = array;
break;
}
}
return lastItem;
}
public static IList<string> SplitPath(string path){
IList<string> result = new List<string>();
Regex reg = new Regex(@"(?!\.)([^. ^\[\]]+)|(?!\[)(\d+)(?=\])");
foreach (Match match in reg.Matches(path))
{
result.Add(match.Value);
}
return result;
}
private static JArray FillEmpty(JArray array, int index)
{
for (int i = 0; i <= index; i++)
{
array.Add(null);
}
return array;
}
private static JSONType GetJSONType(string pathSegment)
{
int x;
return int.TryParse(pathSegment, out x) ? JSONType.ARRAY : JSONType.OBJECT;
}
private static int GetArrayIndex(string pathSegment)
{
int result;
if (int.TryParse(pathSegment, out result))
{
return result;
}
throw new Exception("Unable to parse array index: " + pathSegment);
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With