如何序列化Excel文件流?ClosedXML与Newtonsoft.Json使用遇报错
解决MemoryStream序列化时ReadTimeout报错的问题
问题原因
MemoryStream的ReadTimeout和WriteTimeout属性仅针对异步流场景设计,同步访问时会抛出异常。Newtonsoft.Json默认会序列化对象的所有公共属性,包括这两个不支持同步访问的属性,因此触发报错。
解决方案
方案1:自定义JsonConverter处理MemoryStream
创建自定义转换器,将MemoryStream序列化为字节数组,反序列化时再转回MemoryStream,彻底避开对报错属性的访问:
public class MemoryStreamConverter : JsonConverter<MemoryStream> { public override MemoryStream ReadJson(JsonReader reader, Type objectType, MemoryStream existingValue, bool hasExistingValue, JsonSerializer serializer) { var bytes = serializer.Deserialize<byte[]>(reader); return bytes == null ? new MemoryStream() : new MemoryStream(bytes); } public override void WriteJson(JsonWriter writer, MemoryStream value, JsonSerializer serializer) { var bytes = value.ToArray(); serializer.Serialize(writer, bytes); } }
使用时在序列化配置中添加该转换器:
var settings = new JsonSerializerSettings(); settings.Converters.Add(new MemoryStreamConverter()); var json = JsonConvert.SerializeObject(yourResultObject, settings);
方案2:将MemoryStream转为字节数组存储
修改Result对象,把MemoryStream类型的字段/属性替换为byte[],需要使用流时再从字节数组重建:
public class Result { // 替换原MemoryStream属性为字节数组 public byte[] ExcelData { get; set; } // 可选:提供快速获取流的方法 public MemoryStream GetExcelStream() { return ExcelData == null ? new MemoryStream() : new MemoryStream(ExcelData); } }
生成流时直接转换为字节数组:
using (var workbook = new XLWorkbook(@"C:\Temp\yourfile.xlsx")) using (var stream = new MemoryStream()) { workbook.SaveAs(stream); var result = new Result { ExcelData = stream.ToArray() }; // 序列化Result对象 var json = JsonConvert.SerializeObject(result); }
方案3:忽略报错的属性
通过特性或自定义契约解析器,让Newtonsoft.Json跳过ReadTimeout和WriteTimeout属性的序列化:
方法A:使用JsonIgnore特性
如果Result对象直接包含MemoryStream属性,标记忽略:
public class Result { [JsonIgnore] public MemoryStream ExcelStream { get; set; } }
方法B:自定义ContractResolver排除属性
public class StreamContractResolver : DefaultContractResolver { protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) { var property = base.CreateProperty(member, memberSerialization); if (property.DeclaringType == typeof(MemoryStream) && (property.PropertyName == nameof(MemoryStream.ReadTimeout) || property.PropertyName == nameof(MemoryStream.WriteTimeout))) { property.Ignored = true; } return property; } }
使用时配置序列化设置:
var settings = new JsonSerializerSettings { ContractResolver = new StreamContractResolver() }; var json = JsonConvert.SerializeObject(yourResultObject, settings);
推荐方案
优先选择方案2,字节数组比流更适合序列化存储,避免了流的状态管理问题;如果必须保留MemoryStream类型,则选择方案1的自定义转换器,兼容性更稳定。
内容的提问来源于stack exchange,提问作者Hugo
相关产品推荐
相关产品推荐

