2012-08-13 511 views
47

TL; DR版转换一个JToken(或字符串)给定类型

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应与date_joined给出的值的DateTime对象。

全文

我在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" : "[email protected]", 
    "name" : "Uzair Sajid", 
    "twitter_screen_name" : "UzEE", 
    "join_date" : "2012-08-13T05:30:23Z05+00", 
    "timezone" : 5.5, 
    "access_token" : { 
     "token" : "nkjanIUI8983nkSj)*#)([email protected]", 
     "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>扩展方法的实际代码片段(其基于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实现的方法(并且说实话,文档虽然理解并缺乏一些示例,但还是挺有用的)。

任何帮助真的不胜感激。

+0

任何帮助吗?这是我第二次问SO的任何问题,但从来没有得到答案:/ – 2012-08-13 09:27:13

+0

http://stackoverflow.com/questions/9589218/get-value-from-jtoken-that-may-not-exist-最佳做法 – Guillaume 2012-08-13 12:06:51

+0

@Guillaume谢谢。在发布之前,我已经阅读过。问题是我正在编写一个解析器,它应该能够处理它遇到的任何值,所以一个简单的'jToken.value ()'将不起作用,因为我不知道它会是'double'。我不知道这种类型。 – 2012-08-13 19:32:06

回答

90

现在有一个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;} 
} 
+2

+1这比调用'.ToString()'然后反序列化它要干净得多。我不是百分之百地确定发动机盖下的区别,但是这在句法上更好。 – theyetiman 2015-04-21 10:16:56

+2

如果您不知道类型为设计时间,则会有一个重载版本的ToObject(),它将Type作为参数。我同意。这是最干净的解决方案。这真的应该是“答案”。 – 2016-03-10 22:07:17

22
System.Convert.ChangeType(jtoken.ToString(), targetType); 

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

- 编辑 -

Uzair,这是一个完整的例子,是想告诉你他们的工作

string json = @"{ 
     ""id"" : 77239923, 
     ""username"" : ""UzEE"", 
     ""email"" : ""[email protected]"", 
     ""name"" : ""Uzair Sajid"", 
     ""twitter_screen_name"" : ""UzEE"", 
     ""join_date"" : ""2012-08-13T05:30:23Z05+00"", 
     ""timezone"" : 5.5, 
     ""access_token"" : { 
      ""token"" : ""nkjanIUI8983nkSj)*#)([email protected]"", 
      ""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); 
+3

这两个都不起作用。 'System.Convert.ChangeType()'需要第三个'IFormatProvider'参数。并且'JsonConvert.DeserializeObject()'抛出一个异常:“解析值时遇到意外的字符:U.Path'',第0行,位置0”。 – 2012-08-13 19:54:46

+0

@UzairSajid我在发布之前测试了这些代码。 'IFormatProvider'是可选的。第二部分。如果你发布了你的代码+数据,我可以尝试去处理它。 – 2012-08-13 20:30:09

+2

它没有像你说的那样工作(实际上'System.Convert.ChangeType()'的方法签名在Windows Phone上是不同的),因为它需要强制的第三个'IFormatProvider'参数。但是我可以通过传递'null'作为第三个参数并显式处理一些边缘情况(比如将'1“的值解析为'bool true',将'string'解析为'URI' ) 目前。将看看它是否仍然长期有效。 – 2012-08-14 09:14:27

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

抛出解析异常d ue在第一个参数(我认为)周围缺少引号。我通过添加引号使其工作:

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

我可以用下面的方法来转化为我的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 
} 
相关问题