使用 JSON.NET 将对象序列化为 HttpClient 的响应流

本文关键字:HttpClient 响应 序列化 JSON NET 对象 使用 | 更新日期: 2023-09-27 18:18:27

抽象

嗨,我正在从事一个项目,需要通过 HttpClient 发送某个对象的潜在巨大 json,10-20 MB 的 JSON 是典型的大小。为了有效地做到这一点,我想使用流,既使用 Json.Net 来序列化对象,又使用流来使用 HttpClient 发布数据。

问题

下面是使用 Json.net 进行序列化的代码片段,为了使用流,Json.net 需要一个它将写入的流:

public static void Serialize( object value, Stream writeOnlyStream )
{
    StreamWriter writer = new StreamWriter(writeOnlyStream);  <-- Here Json.net expects the stream to be already created
    JsonTextWriter jsonWriter = new JsonTextWriter(writer);
    JsonSerializer ser = new JsonSerializer();
    ser.Serialize(jsonWriter, value );
    jsonWriter.Flush();
}

虽然 HttpClient 需要一个它将从中读取的流:

using (var client = new HttpClient())
{
    client.BaseAddress = new Uri("http://localhost:54359/");
    var response = await client.PostAsync("/api/snapshot", new StreamContent(readOnlyStream)); <-- The same thing here, HttpClient expects the stream already to exist
    ... 
}

因此,最终这意味着两个类都期望由其他人创建流,但是没有用于 Json.Net 的流,也没有用于HttpClient的流。因此,这个问题似乎可以通过实现一个流来解决,该流将拦截对只读流发出的读取请求,并根据来自只写流的请求发出写入。

问题

也许有人已经偶然发现了这种情况,并且可能已经找到了解决此问题的方法。如果是这样,请与我分享,

提前谢谢你!

使用 JSON.NET 将对象序列化为 HttpClient 的响应流

如果定义 HttpContent 的子类:

public class JsonContent:HttpContent
{
    public object SerializationTarget{get;private set;}
    public JsonContent(object serializationTarget)
    {
        SerializationTarget=serializationTarget;
        this.Headers.ContentType = new MediaTypeHeaderValue("application/json");
    }
    protected override async Task SerializeToStreamAsync(Stream stream, 
                                                TransportContext context)
    {
        using(StreamWriter writer = new StreamWriter(stream))
        using(JsonTextWriter jsonWriter = new JsonTextWriter(writer))
        {
            JsonSerializer ser = new JsonSerializer();
            ser.Serialize(jsonWriter, SerializationTarget );
        }
    }   
    protected override bool TryComputeLength(out long length)
    {
        //we don't know. can't be computed up-front
        length = -1;
        return false;
    }
}

然后,您可以:

var someObj = new {a = 1, b = 2};
var client = new HttpClient();
var content = new JsonContent(someObj);
var responseMsg = await client.PostAsync("http://someurl",content);

序列化程序将直接写入请求流。

使用 PushStreamContent .它不是让 Web API 从流中"拉取",而是让你更直观地"推送"到流中。

object value = ...;
PushStreamContent content = new PushStreamContent((stream, httpContent, transportContext) =>
{
    using (var tw = new StreamWriter(stream))
    {
        JsonSerializer ser = new JsonSerializer();
        ser.Serialize(tw, value);
    }
});

请注意,JSON.NET 在序列化期间不支持异步,因此虽然这可能更节省内存,但它的可伸缩性不高。

不过,我建议尽量避免使用如此大的 JSON 对象。例如,如果您要发送大量集合,请尝试将其切块。许多客户端/服务器在没有特殊处理的情况下会完全拒绝这么大的东西。