gpt4 book ai didi

c# - 将值从字符串转换为流时出错

转载 作者:太空狗 更新时间:2023-10-29 22:25:37 26 4
gpt4 key购买 nike

我想序列化一个自定义对象:

public class MyCustomObject
{
public string Name { get; set; }
public DateTime Date { get; set; }
public List<HttpPostedFileBase> Files { get; set; }

public MyCustomObject()
{
Files = new List<HttpPostedFileBase>();
}
}

在 json 中。为此,我使用自定义转换器:

public class HttpPostedFileConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var stream = (Stream)value;
using (var sr = new BinaryReader(stream))
{
var buffer = sr.ReadBytes((int)stream.Length);
writer.WriteValue(Convert.ToBase64String(buffer));
}
}

我使用 JsonSerializerSettings 序列化为 json.net 知道哪种类型的实现(对于 HttpPostedFileBase)。

        var settings = new JsonSerializerSettings();
settings.Converters.Add(new HttpPostedFileConverter());
settings.TypeNameHandling = TypeNameHandling.Objects;

对象已正​​确序列化,但序列化时出现此错误:

JsonSerializationException Error converting value "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAIBAQEBAQIBAQECAgICAgQDAgICAgUEBAMEBgUGBgYFBgYGBwkIBgcJBwYGCAsICQoKCgoKBggLDAsKDA

这是我的序列化对象的值:

{
"$type": "ConsoleApplication1.MyCustomObject, ConsoleApplication1",
"Name": "Test2",
"Date": "2016-11-03T12:35:14.6020154+01:00",
"Files": [
{
"$type": "System.Web.HttpPostedFileWrapper, System.Web",
"ContentLength": 1024,
"FileName": "Pannigale.jpg",
"ContentType": "image/jpg",
"InputStream": "/9j/4AAQ...KKAP//Z"
}
]
}

反序列化有什么问题?

编辑我已经测试了一个类来测试......现在它可以工作了:

public class TestHttpFile : HttpPostedFileBase
{
string fullFileName = @"C:\Pictures\SBK-1299-Panigale-S_2015_Studio_R_B01_1920x1080.mediagallery_output_image_[1920x1080].jpg";
public override int ContentLength
{
get
{
return 1024;
}
}

public override string FileName
{
get
{
return "Pannigale.jpg";
}
}

public override string ContentType
{
get
{
return "image/jpg";
}
}

public override Stream InputStream
{
get
{
return File.OpenRead(fullFileName);
}
}
}

在连载中我注意到了这个区别:

 "$type": "ConsoleApplication1.TestHttpFile, ConsoleApplication1",

代替

 "$type": "System.Web.HttpPostedFileWrapper, System.Web",

但最后我不想创建包装器或其他任何东西......而且我不明白为什么它适用于这种类型而不适用于 HttpPostedFileWrapper

最佳答案

为什么反序列化失败

简而言之:您要反序列化的类型,即 HttpPostedFileWrapper及其基础HttpPostedFile , 不可公开构建。

大概您的 HttpPostedFileConverter 实际上是一个流转换器,并且工作方式如下:

public class StreamConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(Stream).IsAssignableFrom(objectType);
}

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
var bytes = serializer.Deserialize<byte[]>(reader);
return new MemoryStream(bytes);
}

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var stream = (Stream)value;
var bytes = stream.ReadAllBytesAndReposition();
serializer.Serialize(writer, bytes);
}
}

public static class StreamExtensions
{
public static byte[] ReadAllBytesAndReposition(this Stream stream)
{
const int bufferSize = 4096;
using (var ms = new MemoryStream())
{
byte[] buffer = new byte[bufferSize];
int count;
var position = stream.CanSeek ? stream.Position : (long?)null;
while ((count = stream.Read(buffer, 0, buffer.Length)) != 0)
ms.Write(buffer, 0, count);
if (position != null)
{
// Restore position
stream.Position = position.Value;
}
return ms.ToArray();
}
}
}

在这种情况下,我可以为您的 MyCustomObject 类生成类似于您的 JSON,假设我还设置了 TypeNameHandling = TypeNameHandling.Auto在序列化器设置中。但是,反序列化将失败,因为具体类型 System.Web.HttpPostedFileWrapper 只有一个构造函数,它采用 HttpPostedFile。来自reference source :

public class HttpPostedFileWrapper : HttpPostedFileBase {

private HttpPostedFile _file;

public HttpPostedFileWrapper(HttpPostedFile httpPostedFile) {
if (httpPostedFile == null) {
throw new ArgumentNullException("httpPostedFile");
}
_file = httpPostedFile;
}

由于构造函数是公共(public)的,Json.NET 将调用它,但由于 JSON 中没有名为 httpPostedFile 的属性,它将为该值传递 null,从而导致要抛出的 ArgumentNullException

这给我们带来了下一个问题:HttpPostedFile 没有公共(public)构造函数。 .相反,要直接创建一个,您必须通过反射调用多个不同的 Microsoft 内部方法,如 How to instantiate a HttpPostedFile 中所示。 .因此,即使您创建了一个 custom JsonConverter对于 HttpPostedFileWrapper,在内部构造必需的 HttpPostedFile 非常棘手。 Json.NET 当然不能自动完成。

那么,您有哪些选择?

选项 1:创建您自己的 HttpPostedFilesBase 子类型

您可以创建自己的 HttpPostedFilesBase 子类型,它可以成功序列化和反序列化,然后在序列化期间使用适当的 HttpPostedFilesBase 的所有实例映射到该类型JsonConverter.以下类型 MemoryHttpPostedFile 执行此操作:

public sealed class MemoryHttpPostedFile : HttpPostedFileBase
{
readonly string contentType;
readonly string fileName;
readonly MemoryStream inputStream;

public MemoryHttpPostedFile(string contentType, string fileName, [JsonConverter(typeof(StreamConverter))] MemoryStream inputStream)
{
if (inputStream == null)
throw new ArgumentNullException("inputStream");
this.contentType = contentType;
this.fileName = fileName;
this.inputStream = inputStream;
}

public override int ContentLength { get { return (int)inputStream.Length; } }

public override string ContentType { get { return contentType; } }

public override string FileName { get { return fileName; } }

[JsonConverter(typeof(StreamConverter))]
public override Stream InputStream { get { return inputStream; } }

//TODO: implement SaveAs()
public override void SaveAs(string filename)
{
// Implement based on HttpPostedFile.SaveAs()
// https://referencesource.microsoft.com/#System.Web/HttpPostedFile.cs,678e7f8bc95c149f
throw new NotImplementedException();
}
}

public class HttpPostedFileBaseConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(HttpPostedFileBase).IsAssignableFrom(objectType)
&& !typeof(MemoryHttpPostedFile).IsAssignableFrom(objectType);
}

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var postedFile = (HttpPostedFileBase)value;

// Save position
var wrapper = new MemoryHttpPostedFile(postedFile.ContentType, postedFile.FileName, new MemoryStream(postedFile.InputStream.ReadAllBytesAndReposition()));

serializer.Serialize(writer, wrapper);
}

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
var wrapper = serializer.Deserialize<MemoryHttpPostedFile>(reader);
return wrapper;
}
}

由于 HttpPostedFilesBase 的所有子类型都映射到 MemoryHttpPostedFile,因此不再需要设置 TypeNameHandling.Auto。请注意,我正在使用此答案前面的 StreamConverterStreamExtensions

像这样将转换器添加到设置中:

var settings = new JsonSerializerSettings
{
Converters = new JsonConverter[] { new HttpPostedFileBaseConverter() },
Formatting = Formatting.Indented,
};

选项 2:实际序列化和反序列化一个 HttpPostedFileWrapper

要做到这一点,我们需要使用来自 this answer 的相当复杂和“hacky”的代码至 How to instantiate a HttpPostedFile通过 paracycle ,像这样:

public class HttpPostedFileBaseConverter : JsonConverter
{
class HttpPostedFileSurrogate
{
public string ContentType { get; set; }

public string FileName { get; set; }

public byte[] InputStream { get; set; }
}

public override bool CanConvert(Type objectType)
{
return typeof(HttpPostedFileBase).IsAssignableFrom(objectType);
}

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var wrapper = (HttpPostedFileWrapper)value;

// Save position
var surrogate = new HttpPostedFileSurrogate
{
ContentType = wrapper.ContentType,
FileName = wrapper.FileName,
InputStream = wrapper.InputStream.ReadAllBytesAndReposition(),
};

serializer.Serialize(writer, surrogate);
}

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
var surrogate = serializer.Deserialize<HttpPostedFileSurrogate>(reader);
var file = HttpPostedFileExtensions.ConstructHttpPostedFile(surrogate.InputStream, surrogate.FileName, surrogate.ContentType);
return new HttpPostedFileWrapper(file);
}
}

public static class HttpPostedFileExtensions
{
public static HttpPostedFile ConstructHttpPostedFile(byte[] data, string filename, string contentType)
{
// Adapted from https://stackoverflow.com/questions/5514715/how-to-instantiate-a-httppostedfile/5515134#5515134
// Get the System.Web assembly reference (they seem to be in different assemblies in different versions of .Net
var assemblies = new[] { typeof(HttpPostedFile).Assembly, typeof(HttpPostedFileBase).Assembly };

// Get the types of the two internal types we need
Type typeHttpRawUploadedContent = assemblies.Select(a => a.GetType("System.Web.HttpRawUploadedContent")).Where(t => t != null).First();
Type typeHttpInputStream = assemblies.Select(a => a.GetType("System.Web.HttpInputStream")).Where(t => t != null).First();

// Prepare the signatures of the constructors we want.
Type[] uploadedParams = { typeof(int), typeof(int) };
Type[] streamParams = { typeHttpRawUploadedContent, typeof(int), typeof(int) };
Type[] parameters = { typeof(string), typeof(string), typeHttpInputStream };

// Create an HttpRawUploadedContent instance
object uploadedContent = typeHttpRawUploadedContent
.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, uploadedParams, null)
.Invoke(new object[] { data.Length, data.Length });

// Call the AddBytes method
typeHttpRawUploadedContent
.GetMethod("AddBytes", BindingFlags.NonPublic | BindingFlags.Instance)
.Invoke(uploadedContent, new object[] { data, 0, data.Length });

// This is necessary if you will be using the returned content (ie to Save)
typeHttpRawUploadedContent
.GetMethod("DoneAddingBytes", BindingFlags.NonPublic | BindingFlags.Instance)
.Invoke(uploadedContent, null);

// Create an HttpInputStream instance
object stream = (Stream)typeHttpInputStream
.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, streamParams, null)
.Invoke(new object[] { uploadedContent, 0, data.Length });

// Create an HttpPostedFile instance
HttpPostedFile postedFile = (HttpPostedFile)typeof(HttpPostedFile)
.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, parameters, null)
.Invoke(new object[] { filename, contentType, stream });

return postedFile;
}
}

请注意,我正在使用此答案前面的 StreamExtensions。此版本的 HttpPostedFileBaseConverter 也将像往常一样添加到 JsonSerializerSettings.Converters

(老实说,我不推荐这个解决方案,因为它太依赖于 Microsoft 实现 HttpPostedFile 的未记录的内部结构,这在以后的版本中很容易改变。)

关于c# - 将值从字符串转换为流时出错,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40400799/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com