如何在给定动态列列表的情况下反序列化api响应?C#.NET核心2.2
假设您有一个端点,该端点返回一个JSON响应,其中包含一个索引列的动态列表,格式如下:如何在给定动态列列表的情况下反序列化api响应?C#.NET核心2.2,c#,json,http,asp.net-core,C#,Json,Http,Asp.net Core,假设您有一个端点,该端点返回一个JSON响应,其中包含一个索引列的动态列表,格式如下: "columnNames": [ "date", "value", "someOtherValue" ], "data": [ [ "2019-05-29", 1.23, 2.34 ], [ "2019-05-28", 0.20, 1.34 ], [ "2019-05-27, 2.99, 1.94 ]
"columnNames": [
"date",
"value",
"someOtherValue"
],
"data": [
[
"2019-05-29",
1.23,
2.34
],
[
"2019-05-28",
0.20,
1.34
],
[
"2019-05-27,
2.99,
1.94
]
]
反序列化此类响应的最佳方式是什么?我可以尝试将它映射到包含列名和数据的某个类,然后将其映射出来,大致如下(伪代码):
var-apiResponseContent=wait-response.Content.ReadAsAsync();
foreach(apiResponseContent.data中的var responseData){
var模型=新模型();
model.date=responseData[apiResponseContent.columnNames.First(v=>v==“date”).index]
..
}
但这似乎是这样一种典型的场景,即应该有一种更有效的替代方案,如果端点响应发生变化,该方案不容易中断。当然,我可以使用反射并创建一个扩展方法来自动将列映射到类,但这样做似乎有些奇怪。这是一种特殊的格式,尤其是对于包含各种类型的数据数组。但是,您可以使用这样一个简单的类:
public class ApiResponse
{
public IEnumerable<string> ColumnNames { get; set; }
public IEnumerable<List<object>> Data { get; set; }
}
public class Foo
{
public string Date { get; set; }
public double Value { get; set; }
public double SomeOtherValue { get; set; }
}
var apiResponseContent = await response.Content.ReadAsAsync<ApiResponse>();
var actualData = MapTo<Foo>(apiResponseContent);
公共类响应
{
公共IEnumerable列名称{get;set;}
公共IEnumerable数据{get;set;}
}
如果要将此对象映射到另一组对象,可以使用使用反射的通用函数,例如:
public List<T> MapTo<T>(ApiResponse source)
where T : new()
{
var properties = typeof(T).GetProperties();
foreach (var datum in source.Data)
{
var t = new T();
for(var colIndex = 0; colIndex < source.ColumnNames.Count; colIndex++)
{
var property = properties.SingleOrDefault(p => p.Name.Equals(source.ColumnNames[colIndex], StringComparison.InvariantCultureIgnoreCase));
if (property != null)
{
property.SetValue(t, Convert.ChangeType(datum[colIndex], property.PropertyType));
}
}
yield return t;
}
}
公共列表映射到(ApiResponse源)
其中T:new()
{
var properties=typeof(T).GetProperties();
foreach(source.Data中的var数据)
{
var t=新的t();
对于(var colIndex=0;colIndexp.Name.Equals(source.ColumnNames[colIndex],StringComparison.InvariantCultureIgnoreCase));
if(属性!=null)
{
SetValue(t,Convert.ChangeType(datum[colIndex],property.PropertyType));
}
}
收益率t;
}
}
最终的代码可能如下所示:
public class ApiResponse
{
public IEnumerable<string> ColumnNames { get; set; }
public IEnumerable<List<object>> Data { get; set; }
}
public class Foo
{
public string Date { get; set; }
public double Value { get; set; }
public double SomeOtherValue { get; set; }
}
var apiResponseContent = await response.Content.ReadAsAsync<ApiResponse>();
var actualData = MapTo<Foo>(apiResponseContent);
公共类Foo
{
公共字符串日期{get;set;}
公共双值{get;set;}
公共双精度SomeOtherValue{get;set;}
}
var apiResponseContent=await response.Content.ReadAsAsync();
var actualData=MapTo(apiResponseContent);
我决定使用反射解决方案,但稍微调整了一下。我在项目中使用AutoMapper,因此为了保持体系结构的一致性,我编写了一个ValueResolver,它基本上围绕您的方法展开,只是做了一些调整:
public class VariableColumnConverter : ITypeConverter<ApiResponse, List<AssetPrice>>
{
public List<AssetPrice> Convert(ApiResponse source, List<AssetPrice> destination, ResolutionContext context)
{
var properties = typeof(AssetPrice).GetProperties();
destination = new List<AssetPrice>();
foreach (var dataItem in source.data)
{
var price = new AssetPrice();
foreach (var column in source.columnNames.Select((value, i) => (value, i)))
{
var property = properties.SingleOrDefault(p => p.Name.Equals(column.value, StringComparison.InvariantCultureIgnoreCase));
if (property != null)
{
property.SetValue(price, System.Convert.ChangeType(dataItem[column.i], property.PropertyType));
}
}
destination.Add(price);
}
return destination;
}
}
公共类VariableColumnConverter:ITypeConverter
{
公共列表转换(ApiResponse源、列表目标、ResolutionContext上下文)
{
var properties=typeof(AssetPrice).GetProperties();
目的地=新列表();
foreach(source.data中的var dataItem)
{
var价格=新资产价格();
foreach(source.columnNames.Select中的var列((值,i)=>(值,i)))
{
var property=properties.SingleOrDefault(p=>p.Name.Equals(column.value,StringComparison.InvariantCultureIgnoreCase));
if(属性!=null)
{
SetValue(price,System.Convert.ChangeType(dataItem[column.i],property.PropertyType));
}
}
目的地。添加(价格);
}
返回目的地;
}
}
谢谢@DavidG,我删除了泛型,因为我只有一个这样的端点。我会将你的答案标记为正确,因为我的最终方法有点固执己见,而root使用你的方法
无论如何,您的代码存在一些小问题,使其无法编译,
Root
实际上是apisresponse
,而且由于IEnumerable不包含索引方法的定义,因此不能使用[]运算符。如果你能调整一下就太好了。此外,我个人建议对浮点数使用十进制类型。你看过EF Core吗?我没有使用EF Core,我使用Mongo作为持久数据存储。一个和另一个有什么关系?不知道如果你不多解释一下结构,我们能帮上什么忙。例如,一个响应与另一个响应有何不同?我们可以做出什么样的假设?@PiotrJerzyMamenas你的帖子没有提到MongoAlso,这不是格式良好的JSON。是的,这就是我所说的反射方法。谢谢,让我们看看其他人是否有其他想法。我很确定没有其他方法可以做到这一点,没有内置的反序列化程序,因此您需要使用特定代码或使用反射来处理任何类型。