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

抽象

嗨,我正在开发一个项目,需要通过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 ... } 

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

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

先感谢您!

使用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对象。 例如,如果您要发送大量collections品,请尝试将其分块。 许多客户/服务器都会在没有特殊处理的情况下拒绝这么大的东西。

如果您定义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); 

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