我在C#中有一个json对象(表示为Newtonsoft.Json.Linq.JObject对象),我需要将其展平为字典。让我告诉你一个我的意思的例子:
{
"name": "test",
"father": {
"name": "test2"
"age": 13,
"dog": {
"color": "brown"
}
}
}
这应该产生一个包含以下键值对的字典:
["name"] == "test",
["father.name"] == "test2",
["father.age"] == 13,
["father.dog.color"] == "brown"
我该怎么做?
答案 0 :(得分:23)
JObject jsonObject=JObject.Parse(theJsonString);
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;
});
我有同样的要求将嵌套的json结构展平为字典对象。找到了解决方案here。
答案 1 :(得分:4)
您可以使用https://github.com/jsonfx/jsonfx将json反序列化为动态对象。然后使用ExpandoObject获取所需内容。
public Class1()
{
string json = @"{
""name"": ""test"",
""father"": {
""name"": ""test2"",
""age"": 13,
""dog"": {
""color"": ""brown""
}
}
}";
var reader = new JsonFx.Json.JsonReader();
dynamic output = reader.Read(json);
Dictionary<string, object> dict = new Dictionary<string, object>();
GenerateDictionary((System.Dynamic.ExpandoObject) output, dict, "");
}
private void GenerateDictionary(System.Dynamic.ExpandoObject output, Dictionary<string, object> dict, string parent)
{
foreach (var v in output)
{
string key = parent + v.Key;
object o = v.Value;
if (o.GetType() == typeof(System.Dynamic.ExpandoObject))
{
GenerateDictionary((System.Dynamic.ExpandoObject)o, dict, key + ".");
}
else
{
if (!dict.ContainsKey(key))
{
dict.Add(key, o);
}
}
}
}
答案 2 :(得分:1)
我实际上今天早些时候遇到同样的问题一开始就无法在SO上找到这个问题,最后编写了我自己的扩展方法来返回包含JSON blob的叶节点值的JValue
对象。除了一些改进之外,它与接受的答案类似:
.Count()
醇>
根据您的使用案例,这些可能相关或不相关,但它们适用于我的情况。我写过关于学习在my blog上展平JSON.NET对象的文章。这是我写的扩展方法:
public static class JExtensions
{
public static IEnumerable<JValue> GetLeafValues(this JToken jToken)
{
if (jToken is JValue jValue)
{
yield return jValue;
}
else if (jToken is JArray jArray)
{
foreach (var result in GetLeafValuesFromJArray(jArray))
{
yield return result;
}
}
else if (jToken is JProperty jProperty)
{
foreach (var result in GetLeafValuesFromJProperty(jProperty))
{
yield return result;
}
}
else if (jToken is JObject jObject)
{
foreach (var result in GetLeafValuesFromJObject(jObject))
{
yield return result;
}
}
}
#region Private helpers
static IEnumerable<JValue> GetLeafValuesFromJArray(JArray jArray)
{
for (var i = 0; i < jArray.Count; i++)
{
foreach (var result in GetLeafValues(jArray[i]))
{
yield return result;
}
}
}
static IEnumerable<JValue> GetLeafValuesFromJProperty(JProperty jProperty)
{
foreach (var result in GetLeafValues(jProperty.Value))
{
yield return result;
}
}
static IEnumerable<JValue> GetLeafValuesFromJObject(JObject jObject)
{
foreach (var jToken in jObject.Children())
{
foreach (var result in GetLeafValues(jToken))
{
yield return result;
}
}
}
#endregion
}
然后在我的调用代码中,我只从提交的Path
个对象中提取Value
和JValue
属性:
var jToken = JToken.parse("blah blah json here");
foreach (var jValue in jToken.GetLeafValues()
{
Console.WriteLine("{jValue.Path} = {jValue.Value}");
}
答案 3 :(得分:1)
从.NET Core 3.0开始,JsonDocument
是一种方式(不需要Json.NET)。 我相信这会变得更容易。
using System.Linq;
using System.Text.Json;
(...)
public static Dictionary<string, JsonElement> GetFlat(string json)
{
IEnumerable<(string Path, JsonProperty P)> GetLeaves(string path, JsonProperty p)
=> p.Value.ValueKind != JsonValueKind.Object
? new[] { (Path: path == null ? p.Name : path + "." + p.Name, p) }
: p.Value.EnumerateObject() .SelectMany(child => GetLeaves(path == null ? p.Name : path + "." + p.Name, child));
using (JsonDocument document = JsonDocument.Parse(json)) // Optional JsonDocumentOptions options
return document.RootElement.EnumerateObject()
.SelectMany(p => GetLeaves(null, p))
.ToDictionary(k => k.Path, v => v.P.Value.Clone()); //Clone so that we can use the values outside of using
}
下面是更具表现力的版本。
using System.Linq;
using System.Text.Json;
(...)
var json = @"{
""name"": ""test"",
""father"": {
""name"": ""test2"",
""age"": 13,
""dog"": {
""color"": ""brown""
}
}
}";
var d = GetFlat(json);
var options2 = new JsonSerializerOptions { WriteIndented = true };
Console.WriteLine(JsonSerializer.Serialize(d, options2));
{
"name": "test",
"father.name": "test2",
"father.age": 13,
"father.dog.color": "brown"
}
using System.Linq;
using System.Text.Json;
(...)
static Dictionary<string, JsonElement> GetFlat(string json)
{
using (JsonDocument document = JsonDocument.Parse(json))
{
return document.RootElement.EnumerateObject()
.SelectMany(p => GetLeaves(null, p))
.ToDictionary(k => k.Path, v => v.P.Value.Clone()); //Clone so that we can use the values outside of using
}
}
static IEnumerable<(string Path, JsonProperty P)> GetLeaves(string path, JsonProperty p)
{
path = (path == null) ? p.Name : path + "." + p.Name;
if (p.Value.ValueKind != JsonValueKind.Object)
yield return (Path: path, P: p);
else
foreach (JsonProperty child in p.Value.EnumerateObject())
foreach (var leaf in GetLeaves(path, child))
yield return leaf;
}
答案 4 :(得分:0)
您可以使用JSONPath $..*
获取JSON结构的所有成员,并过滤掉没有子元素的成员以跳过容器属性。
例如
var schemaObject = JObject.Parse(schema);
var values = schemaObject
.SelectTokens("$..*")
.Where(t => !t.HasValues)
.ToDictionary(t => t.Path, t => t.ToString());