【问题标题】:Json to C# object handling dynamic propertiesJson 到 C# 对象处理动态属性
【发布时间】:2021-12-13 06:33:49
【问题描述】:

我正在尝试在 c# 对象中实现 json 结构,并且我正在尝试了解如何根据类型使用正确的对象。例如:

public class RootObject
{
    public string name { get; set; }
    public Content content { get; set; }

}
public class Content
{
    public string id{ get; set; }
    public string type { get; set; }
    public Dictionary<string, Item> child { get; set; }
}

public class Item
{
    public string id { get; set; }
    public string type { get; set; }
    public List<string> model { get; set;}
    public string[] color {get; set;}
}

请注意,这只是一个示例,每个对象都有更多属性。如果 Json 包含 type = "Boy" 我如何生成男孩对象。

示例 JSON:

string json = @"
            {
            'name': 'Object 1',
            'content': {
                'body': {
                    'id': 'body',
                    'type': 'Body'
                },
                'style': {
                    'id': 'style',
                    'type': 'Style'
                },
                'DynamicName-123': {
                    'id': 'DynamicName-123',
                    'type': 'Row'
                    'model': {},
                    'colors': []
                },
                'DynamicName-434': {
                    'id': 'DynamicName-434',
                    'type': 'Column'
                    'model': {},
                    'colors': []
                },
                'DynamicName-223': {
                    'id': 'DynamicName-223',
                    'type': 'Item'
                    'model': {},
                    'colors': []
                }
            }
        }";

【问题讨论】:

  • 您可以更改生成的 json 以包含类型,还是您正在接收的数据?如果您无法更改它,我认为您可能必须实现自己的自定义 JsonConverter 并根据属性覆盖读取方法
  • 是的,json 包括 json 中的类型以及静态的 Id。 @Icepickle
  • 请阅读How to Ask 并展示您的尝试。另外,创建一个minimal reproducible example,包括一些示例 JSON。参见例如How to deserialize a JSON array containing different data types to a single object。您可能需要一个自定义的 JsonConverter,它根据 JSON 中的 type 字符串实例化适当的类型。
  • 你有机会分享一个 json 样本吗?
  • 能否请您展示您的 json 以清楚了解您是否尝试这样做?

标签: c# json json.net


【解决方案1】:

如果您的键/值对不是固定的并且数据必须是可配置的,那么 Newtonsoft.json 有一个可以在这里使用的功能,那就是 [JsonExtensionData] Read more

现在在序列化对象时写入扩展数据。读取和写入扩展数据可以自动往返所有 JSON,而无需将每个属性添加到您要反序列化的 .NET 类型。只声明您感兴趣的属性,让扩展数据完成其余的工作。

在你的情况下,假设有一个类,

public class MyClass
{
    public string Qaz { get; set; }
    public string Wsx { get; set; }

    [JsonExtensionData]
    public Dictionary<string, JToken> child { get; set; }

    public MyClass()
    {
        child = new Dictionary<string, JToken>();
    }
}

在上面的类中,您知道QazWsx 总是存在于您的json 中,它们包含值或空值,

但是对于动态数据,您不能说您将从 json 中收到哪个键/值对,因此 [JsonExtensionData] 可以将所有这些键/值对收集到字典中。

假设以下类将用于您的动态数据,

public class ABC
{
    public string Abc { get; set; }
}

public class PQR
{
    public string Pqr { get; set; }
}

public class XYZ
{
    public string Xyz { get; set; }
}

序列化:

ABC aBC = new ABC { Abc = "abc" };
PQR pQR = new PQR { Pqr = "pqr" };
XYZ xYZ = new XYZ { Xyz = "xyz" };

MyClass myClass = new MyClass();

myClass.Qaz = "qaz";
myClass.Wsx = "wsx";

myClass.child.Add("ABC", JToken.FromObject(aBC));
myClass.child.Add("PQR", JToken.FromObject(pQR));
myClass.child.Add("XYZ", JToken.FromObject(xYZ));

string outputJson = JsonConvert.SerializeObject(myClass);

这会给你类似json

{
  "Qaz": "qaz",
  "Wsx": "wsx",
  "ABC": {
    "Abc": "abc"
  },
  "PQR": {
    "Pqr": "pqr"
  },
  "XYZ": {
    "Xyz": "xyz"
  }
}

反序列化:

MyClass myClass = JsonConvert.DeserializeObject<MyClass>(outputJson);

string Qaz = myClass.Qaz;
string Wsx = myClass.Wsx;

if (myClass.child.ContainsKey("ABC"))
{
    ABC abcObj = myClass.child["ABC"].ToObject<ABC>();
}

if (myClass.child.ContainsKey("PQR"))
{
    PQR pqrObj = myClass.child["PQR"].ToObject<PQR>();
}

if (myClass.child.ContainsKey("XYZ"))
{
    XYZ pqrObj = myClass.child["XYZ"].ToObject<XYZ>();
}

结论: [JsonExtensionData] 的主要目的是让您的 json 类层次结构简单易读,因此您无需为每个属性管理类结构.

获取字典中JToken中特定键的所有动态数据:

您可以使用 LINQ 从上述字典中获取特定键的所有动态数据。

var allAbcTypes = myClass.child
    .SelectMany(x => x.Value
                      .ToObject<JObject>()
                      .Properties()
                      .Where(p => p.Name == "Abc")    //<= Use "Column" instead of "Abc"
                      .Select(o => new ABC            //<= Use your type that contais "Column" as a property
                      {
                           Abc = o.Value.ToString()
                      })).ToList();

在你的情况下,它类似于,

var allColumnTypes = myClass.child
    .SelectMany(x => x.Value
                      .ToObject<JObject>()
                      .Properties()
                      .Where(p => p.Name == "Column")
                      .Select(o => new Item
                      {
                         id = x.Value["id "].ToString(),
                         type = x.Value["type "].ToString(),
                         model = x.Value["model"].ToObject<List<string>>(),
                         color = x.Value["color"].ToObject<string[]>()
                      })).ToList();

【讨论】:

  • 如果我想输出所有男孩对象,我该怎么做?我可以调用类型或 ID,但是对象名称是动态的
  • 让我知道您的 json 中有多少 boy 对象可用,因为在同一级别上 json 不允许您多次添加相同的键,请显示您的示例 json 以获得清晰的想法你想做什么?
  • 我已经用一个例子更新了线程。我只是想像上面的结构一样生成一个对象列表,但在我的追逐中,每个对象都有一个唯一的名称,但属性是相同的。我不能准确地调用 [Jsonproperty()],因为它总是独一无二的
  • 您现在更新的类结构已经用于显示您的 json 的唯一名称,因为您已经使用 Dictionary 并且该字典将处理唯一名称,因为字典不允许重复键名,除非您必须从Content 类中删除idtype。然后一切都会为你工作
  • 或者我想您想知道如何填充生成您在帖子中显示的 json 的对象?
【解决方案2】:

如果您想反序列化为带有键字符串和动态值(在本例中为男孩或女孩)的字典,我知道的唯一方法是使用动态类:

 public List<Dictionary<string, dynamic>> child { get; set; }

【讨论】:

  • 很有趣,这可能是我需要的解决方案,因为我并不真正关心我正在反序列化的 Json,我只是想存储它并从中提取数据。我将如何访问这些数据?
  • 你可以像其他类一样使用它:var dynamic = new Boy(); Console.WriteLine(dynamic.Name) 但在你的情况下,也许最好的选择是强制转换对象(我不确定你会用这个做什么,但我写信给你一个例子):if (element is Boy) { var boy = element as Boy; Your Code in Boy's cases } else if (element is Girl) { var girl = element as Girl; Your code in Girls' cases} 如果你只想序列化对象的其他情况,最好的选择是 JTOKEN,就像 er-ho 说的那样。
猜你喜欢
  • 2016-05-08
  • 1970-01-01
  • 2016-05-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-16
  • 2017-08-27
相关资源
最近更新 更多