抽象
嗨,我正在从事一个项目,需要通过 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的流。因此,这个问题似乎可以通过实现一个流来解决,该流将拦截对只读流发出的读取请求,并根据来自只写流的请求发出写入。
问题
也许有人已经偶然发现了这种情况,并且可能已经找到了解决此问题的方法。如果是这样,请与我分享,
提前谢谢你!
如果定义 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 对象。例如,如果您要发送大量集合,请尝试将其切块。许多客户端/服务器在没有特殊处理的情况下会完全拒绝这么大的东西。