反序列化 JSON 其中 属性 名称表示值的类型

Deserialize JSON where the property name indicates the type of the value

我目前正在处理从外部 API 获取数据的问题。我收到的数据如下所示。 (只是一个模型;不要指望这些值有任何意义。这只是为了说明我得到了什么样的数据。)

{
   "user": [
      {
        "key": "12345678",
        "data": [
          {
            "id": "Name",
            "string": "Bob"
          },
          {
            "id": "ElapsedTimeSinceLastMessage",
            "timestamp": 1618233964000
          },
          {
            "id": "Age",
            "number": 27
          }
        ]
      }
   ]
}

我真的不知道我应该如何反序列化这个 JSON。

我现在用来反序列化的 类 看起来像这样:

public class User
{
    [JsonProperty("key")]
    public string Key { get; set; }

    [JsonProperty("data")]
    public List<DataEntry> DataEntries { get; set; }
}

public class DataEntry
{
    [JsonProperty("id")]
    public string Id { get; set; }

    public Type Value { get; set; }
}

而且我不知道我需要设置什么才能反序列化 DataEntry 中的 Value。也许有人可以指导我正确的方向?

主要问题出在您创建的模型中。

首先,基于 JSON,您需要另一个包含用户列表的 class。

public class ResultClass
{
    public List<User> User { get; set; }
}

之后因为data的第二个对象属性没有常量名,所以我们不能给它指定一个常量名(比如value)。我们应该将数据 属性 定义为一个对象。所以用户 class 应该是这样的:

public class User
{
    [JsonProperty("key")]
    public string Key { get; set; }

    [JsonProperty("data")]
    public List<object> DataEntries { get; set; }
}

最后,在控制器中,你应该反序列化 ResultJson class:

var result = JsonConvert.DeserializeObject<ResultClass>(jsonTxt);

您可以使用 Json to C#。它从您的 json 字符串生成以下 类。如您所见,您还可以使用 可空类型 (long?, int?)。如果有值,则设置所需的变量。否则将其保留为空。这样就可以根据数据的id得到不同的类型了。

public class DataEntry
{
    [JsonProperty("id")]
    public string Id { get; set; }

    [JsonProperty("string")]
    public string String { get; set; }

    [JsonProperty("timestamp")]
    public long? Timestamp { get; set; }

    [JsonProperty("number")]
    public int? Number { get; set; }
}

public class User
{
    [JsonProperty("key")]
    public string Key { get; set; }

    [JsonProperty("data")]
    public List<DataEntry> Data { get; set; }
}

public class Root
{
    public List<User> User { get; set; }
}

要反序列化:

string response = "{\"user\":[{\"key\":\"12345678\",\"data\":[{\"id\":\"Name\",\"string\":\"Bob\"},{\"id\":\"ElapsedTimeSinceLastMessage\",\"timestamp\":1618233964000},{\"id\":\"Age\",\"number\":27}]}]}";
Root myDeserializedClass = JsonConvert.DeserializeObject<Root>(response);

一个攻击角度是字典:

public class WithUser
{
    public List<User> User { get; set; }

}

public class User
{
    [JsonProperty("key")]
    public string Key { get; set; }

    [JsonProperty("data")]
    public List<Dictionary<string,object>> DataEntries { get; set; }
}

拔牙有点痛苦,但有可能:

public static void Main()
{
    var json = File.ReadAllText("Example.json");
    var x = JsonConvert.DeserializeObject<WithUser>(json);

    var user = x.User.Single();
    var age = Extract<long>(user, "Age");
    var name = Extract<string>(user, "Name");
    var elapsedTimeSinceLastMessage = TimeSpan.FromTicks(Extract<long>(user, "ElapsedTimeSinceLastMessage"));
    
}

public static T Extract<T>(User user, string name)
{
    var o = user.DataEntries
        .SingleOrDefault(d => (string)d["id"] == name) // Find the one with age
        .SingleOrDefault(kvp => kvp.Key != "id") // Find the not 'id' value
        .Value; // Take the value  
    return (T)o;
}

这个 JSON 的 Data 部分实际上只是伪装的 Dictionary<string, object>。您可以使用自定义 JsonConverter 将 id/value 对列表转换为该格式以便于使用。

首先,定义这些类:

class RootObject
{
    [JsonProperty("user")]
    public List<User> Users { get; set; }
}

class User
{
    public string Key { get; set; }

    [JsonConverter(typeof(CustomDataConverter))]
    public Dictionary<string, object> Data { get; set; }
}

接下来,定义转换器:

class CustomDataConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(Dictionary<string, object>);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        return JToken.Load(reader)
            .Children<JObject>()
            .ToDictionary(jo => (string)jo["id"],
                          jo => jo.Properties()
                                  .Where(jp => jp.Name != "id" && jp.Value is JValue)
                                  .Select(jp => ((JValue)jp.Value).Value)
                                  .FirstOrDefault());
    }

    public override bool CanWrite => false;

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

然后您可以像这样反序列化并转储数据:

var root = JsonConvert.DeserializeObject<RootObject>(json);
foreach (User user in root.Users)
{
    Console.WriteLine("User Key: " + user.Key);
    foreach (var kvp in user.Data)
    {
        Console.WriteLine(kvp.Key + ": " + kvp.Value);
    }
}

这是一个工作演示:https://dotnetfiddle.net/GIT4dl