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


68

TL; 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应该是一个DateTime对象,其值在中给出date_joined

全文

我在Windows Phone项目中同时使用RestSharp和Json.NET,并且在尝试反序列化REST API的JSON响应时遇到问题。

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

这就是为什么我决定使用Newtonsoft的Json.NET的原因,因为它具有更强大的Json反序列化引擎。不幸的是,它不支持RestSharp之类的模糊属性/字段名(或者我还没有找到),因此当我使用诸如say时,它也无法正确映射到我的CLR实体JsonConvert.DeserializeObject<User>(response.Content)

这是我的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代码,并且我已经能够DeserializeResponse<T>JObject该类型上编写一个通用扩展方法,该方法应返回类型为object的对象T。预期用途是这样的:

var user = JObject.Parse(response.Content).DeserializeResponse<User>();

上面的方法应该解析给定的Json Response到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做到这一点的方法(并且说实话,文档虽然可以理解,但缺少一些示例)。

任何帮助将不胜感激。


这里有什么帮助吗?这是我第二次对SO提出任何问题,但从未得到答案:/
Uzair Sajid 2012年


@Guillaume谢谢。发布前我已经阅读过。问题是我正在编写一个解析器,该解析器应该能够处理它遇到的任何值,因此,jToken.value<double>()由于我不知道它将是一个简单的,所以无法使用简单的解析器double。我之前不知道类型。
Uzair Sajid 2012年

Answers:


127

现在有一个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()然后反序列化它更干净的方法。我不是100%知道引擎盖下有什么区别,但是从语法上讲这要好得多。
theyetiman 2015年

4
如果您不知道类型为设计时间,则有一个ToObject()的重载版本,它将Type作为参数。我同意。这是最干净的解决方案。确实应该是“答案”。
罗伯特·

这似乎仍然不灵活。我需要建立一个任意对象来记录传入的json数据并将其存储为可查询的mongo对象。所以我需要类似jobjectOfTypeString.ToObject<JObject> <-的确切代码不起作用的方法
Vincent Buscarello

^给了我超级有用的错误信息“ Message [string]:”无法将'Newtonsoft.Json.Linq.JValue'类型的对象
强制转换

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

要么

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

- 编辑 -

乌扎尔,这是一个完整的示例,只是为了向您展示他们的工作原理

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);

3
这两个都不起作用。System.Convert.ChangeType()需要第三个IFormatProvider参数。并JsonConvert.DeserializeObject()抛出异常:“解析值:U.Path时遇到意外字符,行0,位置0。”
Uzair Sajid 2012年

@UzairSajid我在发布之前测试了这些代码。IFormatProvider是可选的。对于第二部分。如果您发布代码和数据,我可以尝试对其进行处理。
LB 2012年

3
它并没有按照您所说的那样工作(实际上System.Convert.ChangeType(),Windows Phone的方法签名是不同的),因为它需要一个强制性的第三个IFormatProvider参数。但是我现在可以通过传递null第三个参数并显式处理一些边缘情况(例如解析"1"tobool true和a stringto的值)来使其URI工作。将会看到它是否仍然可以长期运行。
Uzair Sajid 2012年

请考虑将“ ToObject”答案标记为正确的答案,这是一个更好的解决方案。
罗伯特·麦克劳斯

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

由于第一个参数周围的引号缺失(我认为)而引发解析异常。我通过添加引号使它起作用:

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

1

我可以使用以下方法为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
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.