Преобразование JToken (или строки) в заданный тип
TL; DR Version
у меня есть объект типа 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
.
Вся История
я использую как RestSharp, так и Json.NET в проекте Windows Phone, и я застрял при попытке десериализовать ответы JSON из REST API.
то, что я на самом деле пытаюсь сделать, - это написать общий метод, который может легко сопоставить мой ответ JSON с моими сущностями CLR, как вы уже можете сделать с RestSharp. Единственная проблема заключается в том, что реализация RestSharp по умолчанию не работает для меня, и она не может успешно проанализировать JSON, поскольку ответ не всегда возвращает все свойства (я не возвращаю поля, которые являются null
С сервера REST).
вот почему я решил использовать Newtonsoft Json.NET так как он имеет гораздо более мощный JSON десериализующий движок. К сожалению, он не поддерживает нечеткие имена свойств / полей, такие как RestSharp (или я их не нашел), поэтому он также неправильно сопоставляется с моими сущностями CLR, когда я использую что-то вроде say 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>
on JObject
это должно возвращать объект типа 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 (и давайте будем честными, документация-это своего рода понимание и отсутствие некоторых примеров).
любая помощь будет действительно оценили.
4 ответа:
теперь есть метод 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;} }
System.Convert.ChangeType(jtoken.ToString(), targetType);
или
JsonConvert.DeserializeObject(jtoken.ToString(), targetType);
-- EDIT--
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);
var i2 = JsonConvert.DeserializeObject(obj["id"].ToString(), type);
бросает исключение разбора из-за отсутствия кавычек вокруг первого аргумента (я думаю). Я получил его на работу, добавив кавычки:
var i2 = JsonConvert.DeserializeObject("\"" + obj["id"].ToString() + "\"", type);
я смог конвертировать, используя метод ниже для моего 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 }