Newtonsoft.Json反序列化base64图像失败

本文关键字:图像 失败 base64 反序列化 Json Newtonsoft | 更新日期: 2023-09-27 18:04:50

我使用Newtonsoft。Json将webservice的输出反序列化为对象。它工作得很好,直到我添加了一个Bitmap属性到我的类(命名为User)来容纳一个化身。

web服务返回该属性作为Base64字符串,这是预期的。问题是,当我试图将JSON从WS转换回List<User>时,在这段代码中抛出了JsonSerializationException:

// T is IList<User>
response.Content.ReadAsStringAsync().Proceed(
    (readTask) =>
    {
        var json = ((Task<string>)readTask).Result;
        var result = JsonConvert.DeserializeObject<T>(json); //<-- it fails here
         // do stuff! 
     });

异常的输出是:

Error converting value "System.Drawing.Bitmap" to type 'System.Drawing.Bitmap'. Path '[2].Avatar

和查看内部异常:

{"Could not cast or convert from System.String to System.Drawing.Bitmap."}

很明显,它无法解析Base64字符串,但不清楚原因。

有什么想法/解决方案吗?

编辑我知道我可以使用Convert.FromBase64String来获取字节数组并从中加载位图。然后我想更新我的问题,询问如何我可以跳过或手动解析该字段。我想避免,不得不手动解析所有的JSON。这可能吗?

编辑2 我发现了根本问题:JSON在webservice中没有被正确序列化(我不知道为什么)。我以为这是一个不同的问题,但不是。我的web服务只是返回一个字符串"System.Drawing.Bitmap",而不是它的base64内容。因此JsonSerializationException

我一直无法解决这个问题,我找到的唯一解决方案是把我的字段变成byte [] .

Newtonsoft.Json反序列化base64图像失败

将该字段读取为字符串,

使用Convert.FromBase64String

转换为字节数组

使用Bitmap.FromStream(new MemoryStream(bytearray));

获取图像

编辑

您可以使用自定义转换器

执行图像序列化/反序列化
public class AClass
{
    public Bitmap image;
    public int i;
}
Bitmap bmp = (Bitmap)Bitmap.FromFile(@"......");
var json = JsonConvert.SerializeObject(new AClass() { image = bmp, i = 666 }, 
                                       new ImageConverter());
var aclass = JsonConvert.DeserializeObject<AClass>(json, new ImageConverter());

ImageConverter

public class ImageConverter : Newtonsoft.Json.JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(Bitmap);
    }
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var m = new MemoryStream(Convert.FromBase64String((string)reader.Value));
        return (Bitmap)Bitmap.FromStream(m);
    }
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        Bitmap bmp = (Bitmap)value;
        MemoryStream m = new MemoryStream();
        bmp.Save(m, System.Drawing.Imaging.ImageFormat.Jpeg);
        writer.WriteValue(Convert.ToBase64String(m.ToArray()));
    }
}

这就是我的解决方案,我使用了注释

[Serializable]
public class MyClass
{
    [JsonConverter(typeof(CustomBitmapConverter))]
    public Bitmap MyImage { get; set; }

    #region JsonConverterBitmap
    internal class CustomBitmapConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return true;
        }
        //convert from byte to bitmap (deserialize)
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            string image = (string)reader.Value;
            byte[] byteBuffer = Convert.FromBase64String(image);
            MemoryStream memoryStream = new MemoryStream(byteBuffer);
            memoryStream.Position = 0;
            return (Bitmap)Bitmap.FromStream(memoryStream);
        }
        //convert bitmap to byte (serialize)
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            Bitmap bitmap = (Bitmap)value;
            ImageConverter converter = new ImageConverter();
            writer.WriteValue((byte[])converter.ConvertTo(bitmap, typeof(byte[])));
        }
        public static System.Drawing.Imaging.ImageFormat GetImageFormat(Bitmap bitmap)
        {
            ImageFormat img = bitmap.RawFormat;
            if (img.Equals(System.Drawing.Imaging.ImageFormat.Jpeg))
                return System.Drawing.Imaging.ImageFormat.Jpeg;
            if (img.Equals(System.Drawing.Imaging.ImageFormat.Bmp))
                return System.Drawing.Imaging.ImageFormat.Bmp;
            if (img.Equals(System.Drawing.Imaging.ImageFormat.Png))
                return System.Drawing.Imaging.ImageFormat.Png;
            if (img.Equals(System.Drawing.Imaging.ImageFormat.Emf))
                return System.Drawing.Imaging.ImageFormat.Emf;
            if (img.Equals(System.Drawing.Imaging.ImageFormat.Exif))
                return System.Drawing.Imaging.ImageFormat.Exif;
            if (img.Equals(System.Drawing.Imaging.ImageFormat.Gif))
                return System.Drawing.Imaging.ImageFormat.Gif;
            if (img.Equals(System.Drawing.Imaging.ImageFormat.Icon))
                return System.Drawing.Imaging.ImageFormat.Icon;
            if (img.Equals(System.Drawing.Imaging.ImageFormat.MemoryBmp))
                return System.Drawing.Imaging.ImageFormat.MemoryBmp;
            if (img.Equals(System.Drawing.Imaging.ImageFormat.Tiff))
                return System.Drawing.Imaging.ImageFormat.Tiff;
            else
                return System.Drawing.Imaging.ImageFormat.Wmf;
        }
    }
    #endregion

我认为不支持从Base64System.Drawing.Bitmap的反序列化。也许你可以尝试反序列化除Avatar属性以外的所有内容

EDIT FOR已编辑的问题

这里有一个关于如何做到这一点的有趣讨论:JSON。反序列化期间的Net Ignore属性

我认为你能做的最好的是使用Regex从json字符串中删除属性:

var newJsonString = Regex.Replace(jsonString, 
                                  "('',)* '"Avatar'": '"[A-Za-z0-9]+'"", 
                                  String.Empty);

,然后反序列化这个没有Avatar属性的newJsonString

稍后您可以解析原始json字符串以获得base64并构建Bitmap

var avatarBase64 = Regex.Match(
                        Regex.Match(json, "('',)* '"Avatar'": '"[A-Za-z0-9]+'"")
                             .ToString(), 
                        "[A-Za-z0-9]+", RegexOptions.RightToLeft)
                        .ToString();
...
byte[] fromBase64 = Convert.FromBase64String(avatarBase64);
using (MemoryStream ms = new MemoryStream(fromBase64))
{
    Bitmap img = (Bitmap)Image.FromStream(ms);
    result.Avatar = img;
}

您可以改进正则表达式或方法,但这是基本思想。