C# 中使用HttpClient读取大型Json数据集

@[TOC]C#
问题:如何使用HTTP读取大型JSON数据?
在工作中我们经常需要调用api获取数据,这些数据大多数情况下都小型的,比如分页获取数据等。通常不使用HTTP方式传输大型数据。

然而再最近的一工作中,需要调用[Salesforce] 的api传输数据,经过测试发现这个api返回巨量数据,大约50k+,一次性存储和反序列化这没多的JSON数据是不可可能的。

方法1:使用传统一次性读并反序列化。
所以最快能想到的方法是使用api提供的filter条件减少每次加载的数据大小,然后分批加载他们。示例代码如下:(为了演示代码做了简化)

public class Main
{
   
   public async void Main()
  {
       string year="FY22"; //按年度
       string accountIds = "123,124"; //按客户Id
       var data = await LoadApiData()JavaScriptSerializer serializer = new JavaScriptSerializer();
           
       serializer.DeserializeObject(data);//这是一个老式的方法(新方法使用String.Text.Json),仅用于演示目的。这个反序列化过程如果返回的JSON数据特别大,会超过长度限制而报错,同时占用内存非常高。

  }
   
   public async Task<string> LoadApiData(string year,string accountIds)
  {
       string apiUrl = "http://salesforce/api?Year={year}&accountId={accountIds}";
           
       HttpClient client = new HttpClient();
       string jsonResponse = await client.GetStringAsync(apiUrl)
       return jason
       
  }
}

但最后发现数据量依然很大,这种方法再Javascript反序列化时候会导致超过最大长度限制,内存占用大,最终导致运行失败。后来尝试使用更更多的filter去减少返回的数据量,但发现如果这样写,太多的参数导致程序的可维护性会变差,同时你也不能保证返回的数据量是否依然超过限制。

经过研究Salesforce的api发现,他们使用了HTTP协议中的流式传输,以分块传输的大型Json数据。返回的HTTP头部包含Transfer-Encoding:chunk。

这样一来,我们也可以使用HttpClient的流式处理方式来处理返回的数据了,避免了之前的问题,内存占用量也会大大减少。

方法2:使用流式分块读取
//该方法使用了C# 8.0的 IAsyncEnumerable 异步流接口,后面有机会再跟大家解释这个强大的功能。
//这里的作用就是为了异步返回流式读取到的JSON数据。

public async IAsyncEnumerable<string> LoadApiData(string year,string accountIds)
  {
       string apiUrl = "http://salesforce/api?Year={year}&accountId={accountIds}";
           
       HttpClient client = new HttpClient();
       string jsonResponse = await client.GetStreamAsync(apiUrl); //获取响应流,该方法会很快返回。因为HTTP一旦准备好头部就会返回,不会等待所有数据完成后在返回。
       var reader = new StreamReader(jsonResponse);

       var jr = new JsonTextReader(reader); //Newtonsoft JSON读取器,它解决了JSON数组流式返回需要分析json格式的问题。

       while(jr.Read())
      {
           var item = serializer.Deserialize<DataModel>(jr);
           yield return item;
      }
       
  }

//再main方法使用这个异步流返回的结果

public async void Main()
{
   string year="FY22"; //按年度
   string accountIds = "123,124"; //按客户Id
   await foreach(var item in LoadApiData(year,accountIds))
  {
       //处理item。
  }
}

总结
我们通常只认为HTTP之传输少量的数据,但是HTTP 1.1规范中定了可以使用Transfer-Encoding:chunk的方式分块传输大量数据。

所以再需要传输大量数据的情况下(尤其是做上下游系统数据集成分析类应用时)可以使用HTTP流式传输,并流式加载和处理他们。好处是占用内存资源大大减少,并可以处理大量数据传输。

C#使用HttpClient的POST方法传递JSON参数,可以通过以下步骤实现: 1. 创建一个HttpClient对象。 2. 创建一个HttpContent对象,将要传递的JSON参数序列化为字符串,并使用StringContent类将字符串转换为HttpContent对象。 例如: ``` string json = JsonConvert.SerializeObject(data); HttpContent content = new StringContent(json, Encoding.UTF8, "application/json"); ``` 其data是要传递的JSON数据。 3. 创建一个HttpRequestMessage对象,设置请求的方法和请求的内容。 例如: ``` HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, url); request.Content = content; ``` 其url是要请求的地址。 4. 发送请求并获取响应。 例如: ``` HttpResponseMessage response = await client.SendAsync(request); ``` 其client是上面创建的HttpClient对象。 完整的代码示例: ``` using System; using System.Net.Http; using System.Text; using System.Threading.Tasks; using Newtonsoft.Json; namespace ConsoleApp1 { class Program { static async Task Main(string[] args) { HttpClient client = new HttpClient(); var data = new { name = "John", age = 30 }; string json = JsonConvert.SerializeObject(data); HttpContent content = new StringContent(json, Encoding.UTF8, "application/json"); string url = "https://example.com/api"; HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, url); request.Content = content; HttpResponseMessage response = await client.SendAsync(request); string result = await response.Content.ReadAsStringAsync(); Console.WriteLine(result); } } } ``` 注意,此处使用了Newtonsoft.Json库将对象序列化为JSON字符串,需要在项目安装此库。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值