在 RestSharp 中动态反序列化为 属性
Dynamically deserializing to a property in RestSharp
我正在玩 Harvest API,我正在尝试尽可能简单地自动映射实体,不幸的是,当我发出像 GET /projects
这样的请求时,它会生成如下结果:
[{
project: {
name: "Test"
}
},
{
project: {
name: "Test 2"
}]
在 RestSharp 中,我不能直接这样做:
client.Execute<List<Project>>(request)
因为它要寻找一个叫做 Project
的 属性。所以我必须制作另一个具有 属性 的 class,并这样称呼它:
client.Execute<List<ProjectContainer>>(request)
我不想为每个实体制作一个 'container' class,所以我想我找到了一个聪明的解决方案来制作一个我可以在所有实体上使用的 class:
public class ListContainer<T> where T : IHarvestEntity
{
public T Item { get; set; }
}
但是,当然,反序列化器不知道它需要将实体名称(或 "Project")映射到 属性 Item
。在 restsharp 文档中,我发现我可以使用 [DeserializeAs(Name = "CustomProperty")]
来告诉反序列化器将哪个字段映射到此 属性。但是,属性只允许常量,这意味着我不能这样做:
[DeserializeAs(Name = typeof(T).FullName)]
public T Item { get; set; }
有谁知道解决这个问题的巧妙方法吗?所以我不必创建 10 个不同的容器 classes?
我建议您使用 Json 的等效 XPath。使用 Json.NET 您可以 parse 字符串并创建一个动态对象。
和SelectToken you can query values, or using Linq.
代码看起来像这样(我没有测试):
// execute the request
RestResponse response = client.Execute(request);
var content = response.Content; // raw content as string
JObject o = JObject.Parse(content);
IList<string> projectNames = o.SelectToken("project").Select(s => (string)s.name).ToList();
您可以根据需要对路径进行编码或配置。
--- 编辑 ---
这是我测试的示例,将 json 字符串转换为项目列表。
var projects = JArray.Parse(response.Content).Select(r => new Project(r["project"]["name"].Value<string>())).ToList();
为了简单起见,您可以使用 List<dynamic>
并通过一行代码按名称访问 property/properties。
var names = client.Execute<List<dynamic>>(request).Data.Select(
item => item["project"]["name"]).ToList(); // list of names
如果这还不够,那么您可以即兴创作您自己的映射器并提取一个集合,例如Project
个实例:
var projects = client.Execute<List<dynamic>>(request).Data.Select(
item => Map<Project>(item)).ToList(); // list of Project instances
其中 Map
方法可能类似于
public T Map<T>(dynamic item) where T : class
{
// inline for clarity
var mappings = new Dictionary<Type,Func<dynamic,object>>
{
{ typeof(Project), map => new Project(map["project"]["name"]) }
};
return (T)mappings[typeof(T)].Invoke(item);
}
给定 Project
定义为
public class Project
{
public Project(string name)
{
Name = name;
}
public string Name { get; set; }
}
我正在玩 Harvest API,我正在尝试尽可能简单地自动映射实体,不幸的是,当我发出像 GET /projects
这样的请求时,它会生成如下结果:
[{
project: {
name: "Test"
}
},
{
project: {
name: "Test 2"
}]
在 RestSharp 中,我不能直接这样做:
client.Execute<List<Project>>(request)
因为它要寻找一个叫做 Project
的 属性。所以我必须制作另一个具有 属性 的 class,并这样称呼它:
client.Execute<List<ProjectContainer>>(request)
我不想为每个实体制作一个 'container' class,所以我想我找到了一个聪明的解决方案来制作一个我可以在所有实体上使用的 class:
public class ListContainer<T> where T : IHarvestEntity
{
public T Item { get; set; }
}
但是,当然,反序列化器不知道它需要将实体名称(或 "Project")映射到 属性 Item
。在 restsharp 文档中,我发现我可以使用 [DeserializeAs(Name = "CustomProperty")]
来告诉反序列化器将哪个字段映射到此 属性。但是,属性只允许常量,这意味着我不能这样做:
[DeserializeAs(Name = typeof(T).FullName)]
public T Item { get; set; }
有谁知道解决这个问题的巧妙方法吗?所以我不必创建 10 个不同的容器 classes?
我建议您使用 Json 的等效 XPath。使用 Json.NET 您可以 parse 字符串并创建一个动态对象。
和SelectToken you can query values, or using Linq.
代码看起来像这样(我没有测试):
// execute the request
RestResponse response = client.Execute(request);
var content = response.Content; // raw content as string
JObject o = JObject.Parse(content);
IList<string> projectNames = o.SelectToken("project").Select(s => (string)s.name).ToList();
您可以根据需要对路径进行编码或配置。
--- 编辑 ---
这是我测试的示例,将 json 字符串转换为项目列表。
var projects = JArray.Parse(response.Content).Select(r => new Project(r["project"]["name"].Value<string>())).ToList();
为了简单起见,您可以使用 List<dynamic>
并通过一行代码按名称访问 property/properties。
var names = client.Execute<List<dynamic>>(request).Data.Select(
item => item["project"]["name"]).ToList(); // list of names
如果这还不够,那么您可以即兴创作您自己的映射器并提取一个集合,例如Project
个实例:
var projects = client.Execute<List<dynamic>>(request).Data.Select(
item => Map<Project>(item)).ToList(); // list of Project instances
其中 Map
方法可能类似于
public T Map<T>(dynamic item) where T : class
{
// inline for clarity
var mappings = new Dictionary<Type,Func<dynamic,object>>
{
{ typeof(Project), map => new Project(map["project"]["name"]) }
};
return (T)mappings[typeof(T)].Invoke(item);
}
给定 Project
定义为
public class Project
{
public Project(string name)
{
Name = name;
}
public string Name { get; set; }
}