如何在 C# 中取消扁平化 json
How to unflatten flattened json in C#
从此 Answer 我学会了如何在 C# 中展平 JSON 对象。
来自 JSON 字符串:
{"menu": {
"id": "file",
"value": "File",
"popup": {
"menuitem": [
{"value": "New", "onclick": "CreateNewDoc()"},
{"value": "Open", "onclick": "OpenDoc()"},
{"value": "Close", "onclick": "CloseDoc()"}
]
}
}}
收件人:
下面是几行字符串,不是对象
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()
现在,我想反转这个过程。
我可以从这个 question 中找到实现,但它在 JavaScript.
中
如何在 C# 中用 json.net 展开 (return 从行结构化 JSON)?
要展平 JSON 对象:
arrayJSON.stringify()
我设法解决了。
下面是我的代码结合Sarath Rachuri's flattening code。
我没有在太多情况下测试它,所以它可能是错误的。
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);
}
}
}
纯粹System.Text.Json unflatting JSON 的解决方案。需要 .Net 6.
private static JsonNode Unflatten(Dictionary<string, JsonValue> source)
{
var regex = new System.Text.RegularExpressions.Regex(@"(?!\.)([^. ^\[\]]+)|(?!\[)(\d+)(?=\])");
JsonNode node = JsonNode.Parse("{}");
foreach (var keyValue in source)
{
var pathSegments = regex.Matches(keyValue.Key).Select(m => m.Value).ToArray();
for (int i = 0; i < pathSegments.Length; i++)
{
var currentSegmentType = GetSegmentKind(pathSegments[i]);
if (currentSegmentType == JsonValueKind.Object)
{
if (node[pathSegments[i]] == null)
{
if (pathSegments[i] == pathSegments[pathSegments.Length - 1])
{
node[pathSegments[i]] = keyValue.Value;
node = node.Root;
}
else
{
var nextSegmentType = GetSegmentKind(pathSegments[i + 1]);
if (nextSegmentType == JsonValueKind.Object)
{
node[pathSegments[i]] = JsonNode.Parse("{}");
}
else
{
node[pathSegments[i]] = JsonNode.Parse("[]");
}
node = node[pathSegments[i]];
}
}
else
{
node = node[pathSegments[i]];
}
}
else
{
if (!int.TryParse(pathSegments[i], out int index))
{
throw new Exception("Cannot parse index");
}
while (node.AsArray().Count - 1 < index)
{
node.AsArray().Add(null);
}
if (i == pathSegments.Length - 1)
{
node[index] = keyValue.Value;
node = node.Root;
}
else
{
if (node[index] == null)
{
var nextSegmentType = GetSegmentKind(pathSegments[i + 1]);
if (nextSegmentType == JsonValueKind.Object)
{
node[index] = JsonNode.Parse("{}");
}
else
{
node[index] = JsonNode.Parse("[]");
}
}
node = node[index];
}
}
}
}
return node;
}
private static JsonValueKind GetSegmentKind(string pathSegment) =>
int.TryParse(pathSegment, out _) ? JsonValueKind.Array : JsonValueKind.Object;
从此 Answer 我学会了如何在 C# 中展平 JSON 对象。 来自 JSON 字符串:
{"menu": {
"id": "file",
"value": "File",
"popup": {
"menuitem": [
{"value": "New", "onclick": "CreateNewDoc()"},
{"value": "Open", "onclick": "OpenDoc()"},
{"value": "Close", "onclick": "CloseDoc()"}
]
}
}}
收件人:
下面是几行字符串,不是对象
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()
现在,我想反转这个过程。 我可以从这个 question 中找到实现,但它在 JavaScript.
中如何在 C# 中用 json.net 展开 (return 从行结构化 JSON)?
要展平 JSON 对象:
arrayJSON.stringify()
我设法解决了。
下面是我的代码结合Sarath Rachuri's flattening code。
我没有在太多情况下测试它,所以它可能是错误的。
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);
}
}
}
纯粹System.Text.Json unflatting JSON 的解决方案。需要 .Net 6.
private static JsonNode Unflatten(Dictionary<string, JsonValue> source)
{
var regex = new System.Text.RegularExpressions.Regex(@"(?!\.)([^. ^\[\]]+)|(?!\[)(\d+)(?=\])");
JsonNode node = JsonNode.Parse("{}");
foreach (var keyValue in source)
{
var pathSegments = regex.Matches(keyValue.Key).Select(m => m.Value).ToArray();
for (int i = 0; i < pathSegments.Length; i++)
{
var currentSegmentType = GetSegmentKind(pathSegments[i]);
if (currentSegmentType == JsonValueKind.Object)
{
if (node[pathSegments[i]] == null)
{
if (pathSegments[i] == pathSegments[pathSegments.Length - 1])
{
node[pathSegments[i]] = keyValue.Value;
node = node.Root;
}
else
{
var nextSegmentType = GetSegmentKind(pathSegments[i + 1]);
if (nextSegmentType == JsonValueKind.Object)
{
node[pathSegments[i]] = JsonNode.Parse("{}");
}
else
{
node[pathSegments[i]] = JsonNode.Parse("[]");
}
node = node[pathSegments[i]];
}
}
else
{
node = node[pathSegments[i]];
}
}
else
{
if (!int.TryParse(pathSegments[i], out int index))
{
throw new Exception("Cannot parse index");
}
while (node.AsArray().Count - 1 < index)
{
node.AsArray().Add(null);
}
if (i == pathSegments.Length - 1)
{
node[index] = keyValue.Value;
node = node.Root;
}
else
{
if (node[index] == null)
{
var nextSegmentType = GetSegmentKind(pathSegments[i + 1]);
if (nextSegmentType == JsonValueKind.Object)
{
node[index] = JsonNode.Parse("{}");
}
else
{
node[index] = JsonNode.Parse("[]");
}
}
node = node[index];
}
}
}
}
return node;
}
private static JsonValueKind GetSegmentKind(string pathSegment) =>
int.TryParse(pathSegment, out _) ? JsonValueKind.Array : JsonValueKind.Object;