反序列化 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
我目前正在处理从外部 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