【问题标题】:Deserialize json with dynamic objects that starts with pattern使用以模式开头的动态对象反序列化 json
【发布时间】:2021-09-08 13:30:45
【问题描述】:

我正在尝试反序列化一些看起来像这样的 json

{
   "id":"2021",
   "descriptions_bg":[
      "30231300",
      "30233160",
      "32420000",
      "30214000"
   ],
   "descriptions_cs":[
      "30231300",
      "30233160",
      "32420000",
      "30214000"
   ],
   "descriptions_da":[
      "30231300",
      "30233160",
      "32420000",
      "30214000"
   ],
   "official_title_bg":"П",
   "official_title_cs":"P",
   "official_title_da":"P",
   "auth_town_bg":"AuthTown",
   "auth_town_cs":"AuthTown",
   "auth_town_da":"AuthTown"
}

这里的问题是,descriptions_*official_title_*auth_town_* 中可能有无数个不同结尾的项目。

我尝试在 C# 中创建像 public string official_title_* { get; set; }这样的类和成员,以便使用 Newtonsoft Json 反序列化,但这(当然)不起作用。

无论如何要解决这个问题?

【问题讨论】:

标签: c# .net json.net


【解决方案1】:

对您来说最好的选择是反序列化为Dictionary<string, JToken> 并基于KeyObject Type 编写业务逻辑。结合Linq可以过滤Key

var json = File.ReadAllText("json1.json");

var dict = JsonConvert.DeserializeObject<Dictionary<string, JToken>>(json);

foreach(var item in dict.Where(x=>x.Key.StartsWith("descriptions_")))
{
    Console.WriteLine($"{item.Key} is type {item.Value.GetType()}");
}

foreach (var item in dict.Where(x => x.Key.StartsWith("auth_town_")))
{
    Console.WriteLine($"{item.Key} is type {item.Value.GetType()}");
}

【讨论】:

  • 我可能会使用JToken 而不是object,但这会起作用。
  • @DavidG 有道理,我已经用 JToken 更新了帖子
  • 谢谢。那么每个键,包括已知键(例如 id)都需要这样处理吗?
【解决方案2】:

另一种方法,使用一个类对象,该对象定义具有公共前缀的每组属性为Dictionary&lt;string, TValue&gt;

使用自定义 JsonConverter 将 JSON 反序列化,该自定义 JsonConverter 使用 Dictionary&lt;string, string&gt; 将 JSON 中的每组属性映射到类的属性:

Dictionary<string, string> map = new Dictionary<string, string>() {
    ["Descriptions"] = "descriptions_",
    ["OfficialTitles"] = "official_title_",
    ["AuthTowns"] = "auth_town_"
};

JSON 中映射到类中某个 Property 的所有属性都添加到相应的 Dictionary 中,并将值转换为类属性定义的 Type。

注意:您需要使映射器和类属性适应 JSON。您可以使用一些逻辑来确定 JSON 中的属性名称何时属于同一 并生成一个新的Dictionary&lt;string, TValue&gt; 以添加到类中,从而使该过程更加通用反射。
或者,如果您发现其他组,只需将更多属性添加到类离线

将其称为: (可能给班级起一个不那么愚蠢的名字:)

var result = new SimpleSequences().Deserialize(json);

助手类对象:

private class SimpleSequences
{
    public SimpleSequences() {
        Descriptions = new Dictionary<string, long[]>();
        OfficialTitles = new Dictionary<string, string>();
        AuthTowns = new Dictionary<string, string>();
    }

    public List<SimpleSequences> Deserialize(string json)
    {
        var options = new JsonSerializerSettings() {
            Converters = new[] { new SimpleSequencesConverter() }
        };
        return JsonConvert.DeserializeObject<List<SimpleSequences>>(json, options);
    }

    public int Id { get; set; }
    public Dictionary<string, long[]> Descriptions { get; set; }
    public Dictionary<string, string> OfficialTitles { get; set; }
    public Dictionary<string, string> AuthTowns { get; set; }
}

自定义 JsonConverter:

此转换器处理单个对象或对象数组(如您的问题所示),但始终返回 List&lt;SimpleSequences&gt;(可能包含单个对象)。根据需要修改。

public class SimpleSequencesConverter : JsonConverter
{
    public override bool CanConvert(Type objectType) => true;

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType != JsonToken.StartArray && reader.TokenType != JsonToken.StartObject) {
            throw new Exception($"Unexpected Type. Expecting Array or Object, got {reader.TokenType}");
        }
        var elementsList = new List<SimpleSequences>();

        if (reader.TokenType == JsonToken.StartObject) {
            elementsList.Add(GetPropertyValues(JObject.Load(reader)));
        }
        else {
            while (reader.Read() && reader.TokenType != JsonToken.EndArray) {
                elementsList.Add(GetPropertyValues(JObject.Load(reader)));
            }
        }
        return elementsList;
    }

    private SimpleSequences GetPropertyValues(JToken token)
    {
        var element = new SimpleSequences();
        element.Id = token["id"].ToObject<int>();
        var descriptions = token.Children().Where(t => t.Path.StartsWith(map["Descriptions"]));

        foreach (JProperty p in descriptions) {
            element.Descriptions.Add(p.Path, p.Value.ToObject<long[]>());
        }

        var titles = token.Children().Where(t => t.Path.StartsWith(map["OfficialTitles"]));
        foreach (JProperty p in titles) {
            element.OfficialTitles.Add(p.Path, p.Value.ToString());
        }

        var authTowns = token.OfType<JToken>().Where(t => t.Path.StartsWith(map["AuthTowns"]));
        foreach (JProperty p in authTowns) {
            element.AuthTowns.Add(p.Path, p.Value.ToString());
        }
        return element;
    }

    public override bool CanWrite => false;

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        => throw new NotImplementedException();

    Dictionary<string, string> map = new Dictionary<string, string>() {
        ["Descriptions"] = "descriptions_",
        ["OfficialTitles"] = "official_title_",
        ["AuthTowns"] = "auth_town_"
    };
}

【讨论】:

    【解决方案3】:

    另一种解决方案是使用多个 LINQ2JSON 查询。

    例如,如果您想在一个列表中获取所有 description_xyz 集合
    那么你可以这样做:

    var semiParsedJson = JObject.Parse(json);
    
    var descriptionProperties = semiParsedJson.Properties()
        .Where(prop => prop.Name.StartsWith("descriptions_"));
    
    var collectionOfDescriptions =
        from desc in descriptionProperties.Values()
        select ((JArray)desc).ToObject<IEnumerable<string>>();
    
    var flattenedDescriptions = collectionOfDescriptions.
        SelectMany(desc => desc)
        .ToList();
    
    • 我们可以使用JObject.Parse半解析json
    • 我们可以根据属性的Name字段进行一些过滤
    • 然后我们可以从每个description_xyz 集合中获取所有值作为IEnumerable&lt;string&gt;
    • 此时我们将拥有一个集合集合IEnumerable&lt;IEnumerable&lt;string&gt;&gt;,我们可以用SelectMany 将其展平
    • 最后我们可以用ToList实现查询

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-04
      • 2011-05-30
      • 1970-01-01
      • 2021-04-18
      • 2021-11-14
      相关资源
      最近更新 更多