Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/json/15.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在给定动态列列表的情况下反序列化api响应?C#.NET核心2.2_C#_Json_Http_Asp.net Core - Fatal编程技术网

如何在给定动态列列表的情况下反序列化api响应?C#.NET核心2.2

如何在给定动态列列表的情况下反序列化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 ]

假设您有一个端点,该端点返回一个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
  ]
]
反序列化此类响应的最佳方式是什么?我可以尝试将它映射到包含列名和数据的某个类,然后将其映射出来,大致如下(伪代码):

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。是的,这就是我所说的反射方法。谢谢,让我们看看其他人是否有其他想法。我很确定没有其他方法可以做到这一点,没有内置的反序列化程序,因此您需要使用特定代码或使用反射来处理任何类型。