将 JToken (或字符串)转换为给定的 Type

DR 版本

我有一个类型为 JToken(但也可以是 string)的对象,我需要将其转换为包含在 type变量中的 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应该是一个具有 date_joined中给出的值的 DateTime 对象。

完整的故事

我在 Windows Phone 项目中同时使用 RestSharp 和 JSON.NET,在尝试从 REST API 反序列化 JSON 响应时遇到了困难。

我实际上想要完成的是编写一个通用方法,它可以轻松地将 JSON 响应映射到 CLR 实体中,就像您已经可以使用 RestSharp 一样。唯一的问题是,默认的 RestSharp 实现对我来说不起作用,它无法成功地解析 JSON,因为响应并不总是返回所有的属性(我不会从 REST 服务器返回 null字段)。

这就是为什么我决定使用 Newton 的 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 Response to a User 实体对象。下面是我在 DeserializeResponse<User>扩展方法中所做的实际代码片段(它基于 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 实现这一点的方法(老实说,文档有点难以理解,而且缺乏一些示例)。

如果你能帮忙,我会很感激的。

147265 次浏览
System.Convert.ChangeType(jtoken.ToString(), targetType);

or

JsonConvert.DeserializeObject(jtoken.ToString(), targetType);

--EDIT--

Uzair, Here is a complete example just to show you they work

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);
var i2 = JsonConvert.DeserializeObject(obj["id"].ToString(), type);

throws a parsing exception due to missing quotes around the first argument (I think). I got it to work by adding the quotes:

var i2 = JsonConvert.DeserializeObject("\"" + obj["id"].ToString() + "\"", type);

There is a ToObject method now.

var obj = jsonObject["date_joined"];
var result = obj.ToObject<DateTime>();

It also works with any complex type, and obey to JsonPropertyAttribute rules

var result = obj.ToObject<MyClass>();


public class MyClass
{
[JsonProperty("date_field")]
public DateTime MyDate {get;set;}
}

I was able to convert using below method for my 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
}