将 JToken(或字符串)转换为给定的类型
Posted
技术标签:
【中文标题】将 JToken(或字符串)转换为给定的类型【英文标题】:Converting a JToken (or string) to a given Type 【发布时间】:2012-08-09 06:13:16 【问题描述】:TL;DR 版本
我有一个JToken
类型的对象(但也可以是string
),我需要将其转换为包含在type
变量中的类型:
Type type = typeof(DateTime); /* can be any other Type like string, ulong etc */
var obj = jsonObject["date_joined"]; /* contains 2012-08-13T06:01:23Z+05:00 */
var result = Some_Way_To_Convert(type, obj);
上面的result
应该是一个DateTime对象,其值在date_joined
中给出。
全文
我在 Windows Phone 项目中同时使用 RestSharp 和 Json.NET,但在尝试反序列化来自 REST API 的 JSON 响应时遇到了问题。
我实际上想要完成的是编写一个通用方法,该方法可以轻松地将我的 JSON 响应映射到我的 CLR 实体中,就像您已经可以使用 RestSharp 一样。唯一的问题是默认的 RestSharp 实现对我不起作用,它无法成功解析 JSON,因为响应并不总是返回所有属性(我不返回来自 REST 服务器的 null
字段)。
这就是我决定使用 Newtonsoft 的 Json.NET 的原因,因为它具有更强大的 Json 反序列化引擎。不幸的是,它不支持像 RestSharp 之类的模糊属性/字段名称(或者我没有找到任何名称),因此当我使用 JsonConvert.DeserializeObject<User>(response.Content)
之类的东西时,它也无法正确映射到我的 CLR 实体。
这是我的 Json 的样子(实际上是一个示例):
"id" : 77239923,
"username" : "UzEE",
"email" : "uzee@email.net",
"name" : "Uzair Sajid",
"twitter_screen_name" : "UzEE",
"join_date" : "2012-08-13T05:30:23Z05+00",
"timezone" : 5.5,
"access_token" :
"token" : "nkjanIUI8983nkSj)*#)(kjb@K",
"scope" : [ "read", "write", "bake pies" ],
"expires" : 57723
,
"friends" : [
"id" : 2347484",
"name" : "Bruce Wayne"
,
"id" : 996236,
"name" : "Clark Kent"
]
这是我的 CLR 实体的示例:
class AccessToken
public string Token get; set;
public int Expires get; set;
public string[] Scope get; set;
public string Secret get; set; /* may not always be returned */
class User
public ulong Id get; set;
public string UserName get; set;
public string Email get; set;
public string Name get; set;
public string TwitterScreenName get; set;
public DateTime JoinDate get; set;
public float Timezone get; set;
public bool IsOnline get; set; /* another field that might be blank e.g. */
public AccessToken AccessToken get; set;
public List<User> Friends get; set;
我想要的是一种将上述 JSON 解析为给定 CLR 对象的简单方法。我查看了 RestSharp 源代码并看到了JsonDeserializer
代码,并且我已经能够在JObject
上编写一个通用扩展方法DeserializeResponse<T>
,它应该返回一个T
类型的对象。预期用途是这样的:
var user = JObject.Parse(response.Content).DeserializeResponse<User>();
上述方法应该将给定的 Json 响应解析为用户实体对象。这是我在 DeserializeResponse<User>
扩展方法中所做的实际代码 sn-p(它基于 RestSharp 代码):
public static T DeserializeResponse<T>(this JObject obj) where T : new()
T result = new T();
var props = typeof(T).GetProperties().Where(p => p.CanWrite).ToList();
var objectDictionary = obj as IDictionary<string, JToken>;
foreach (var prop in props)
var name = prop.Name.GetNameVariants(CultureInfo.CurrentCulture).FirstOrDefault(n => objectDictionary.ContainsKey(n));
var value = name != null ? obj[name] : null;
if (value == null) continue;
var type = prop.PropertyType;
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
type = type.GetGenericArguments()[0];
// This is a problem. I need a way to convert JToken value into an object of Type type
prop.SetValue(result, ConvertValue(type, value), null);
return result;
我猜转换应该是一件非常简单的事情,因为它是一项微不足道的任务。但是我已经搜索了很长一段时间,但仍然没有找到通过 Json.NET 实现它的方法(老实说,文档虽然有点理解并且缺少一些示例)。
任何帮助将不胜感激。
【问题讨论】:
这里有什么帮助吗?这是我第二次就 SO 提出任何问题但从未得到答案:/ ***.com/questions/9589218/… @Guillaume 谢谢。我在发帖之前已经读过了。问题是我正在编写一个解析器,它应该能够处理它遇到的任何值,所以一个简单的jToken.value<double>()
将不起作用,因为我不知道它会是一个double
。我不知道手头的类型。
【参考方案1】:
现在有一个 ToObject 方法。
var obj = jsonObject["date_joined"];
var result = obj.ToObject<DateTime>();
它也适用于任何复杂类型,并遵守 JsonPropertyAttribute 规则
var result = obj.ToObject<MyClass>();
public class MyClass
[JsonProperty("date_field")]
public DateTime MyDate get;set;
【讨论】:
+1 这比调用.ToString()
然后反序列化要干净得多。我不是 100% 确定引擎盖下的区别是什么,但这在语法上要好得多。
如果您不知道设计时的类型,有一个 ToObject() 的重载版本,它以 Type 作为参数。我同意。这是最干净的解决方案。它真的应该是“答案”。
这似乎仍然不灵活。我需要构建一个任意对象来记录传入的 json 数据并将其存储为可查询的 mongo 对象。所以我需要像jobjectOfTypeString.ToObject<JObject>
这样的东西
^ 给了我超级信息和有用的错误“消息 [字符串]:”无法将类型为 'Newtonsoft.Json.Linq.JValue' 的对象转换为类型 'Newtonsoft.Json.Linq.JObject' ." "【参考方案2】:
System.Convert.ChangeType(jtoken.ToString(), targetType);
或
JsonConvert.DeserializeObject(jtoken.ToString(), targetType);
--编辑--
Uzair,这是一个完整的示例,只是为了向您展示他们的工作原理
string json = @"
""id"" : 77239923,
""username"" : ""UzEE"",
""email"" : ""uzee@email.net"",
""name"" : ""Uzair Sajid"",
""twitter_screen_name"" : ""UzEE"",
""join_date"" : ""2012-08-13T05:30:23Z05+00"",
""timezone"" : 5.5,
""access_token"" :
""token"" : ""nkjanIUI8983nkSj)*#)(kjb@K"",
""scope"" : [ ""read"", ""write"", ""bake pies"" ],
""expires"" : 57723
,
""friends"" : [
""id"" : 2347484,
""name"" : ""Bruce Wayne""
,
""id"" : 996236,
""name"" : ""Clark Kent""
]
";
var obj = (JObject)JsonConvert.DeserializeObject(json);
Type type = typeof(int);
var i1 = System.Convert.ChangeType(obj["id"].ToString(), type);
var i2 = JsonConvert.DeserializeObject(obj["id"].ToString(), type);
【讨论】:
这两个都不起作用。System.Convert.ChangeType()
需要第三个 IFormatProvider
参数。而JsonConvert.DeserializeObject()
则抛出异常:“解析值时遇到意外字符:U. Path '', line 0, position 0。”
@UzairSajid 我在发布之前测试了这些代码。 IFormatProvider
是可选的。对于第二部分。如果您发布代码+数据,我可以尝试处理它。
它并没有像你说的那样工作(实际上System.Convert.ChangeType()
的方法签名在Windows Phone 上是不同的),因为它需要一个强制性的第三个IFormatProvider
参数。但是我现在可以通过传递null
作为第三个参数并显式处理一些边缘情况(例如将"1"
的值解析为bool true
和将string
的值解析为URI
)来使其工作.看看它是否仍然有效。
请考虑将“ToObject”答案标记为正确答案,这是一个更好的解决方案。【参考方案3】:
var i2 = JsonConvert.DeserializeObject(obj["id"].ToString(), type);
由于第一个参数周围缺少引号(我认为)而引发解析异常。我通过添加引号使其工作:
var i2 = JsonConvert.DeserializeObject("\"" + obj["id"].ToString() + "\"", type);
【讨论】:
【参考方案4】:我能够为我的 WebAPI 使用以下方法进行转换:
[HttpPost]
public HttpResponseMessage Post(dynamic item) // Passing parameter as dynamic
JArray itemArray = item["Region"]; // You need to add JSON.NET library
JObject obj = itemArray[0] as JObject; // Converting from JArray to JObject
Region objRegion = obj.ToObject<Region>(); // Converting to Region object
【讨论】:
以上是关于将 JToken(或字符串)转换为给定的类型的主要内容,如果未能解决你的问题,请参考以下文章
无法将类型“newtonsoft.json.linq.jtoken”隐式转换为 newt“newtonsoft.json.linq.jvalue”
将 JSON 解析为 JToken 时如何将所有键更改为小写
从 JSON 检索项目时获取“无法将 Newtonsoft.Json.Linq.JObject 转换为 Newtonsoft.Json.Linq.JToken”