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

Lu4*_*Lu4 2 c# json json.net

抽象

嗨,我正在开发一个项目,需要通过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();
}
Run Code Online (Sandbox Code Playgroud)

虽然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

    ... 
}
Run Code Online (Sandbox Code Playgroud)

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

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

先感谢您!

spe*_*der 11

如果您定义了以下的子类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;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你可以:

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

并且序列化器将直接写入请求流.


Cor*_*son 5

使用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);
    }
});
Run Code Online (Sandbox Code Playgroud)

请注意,JSON.NET 在序列化过程中不支持异步,因此虽然这可能会提高内存效率,但它的可扩展性不太好。

不过,我建议尝试避免使用如此大的 JSON 对象。例如,如果您要发送大量集合,请尝试将其分成块。如果没有特殊处理,许多客户端/服务器会断然拒绝如此大的东西。