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

时间:2013-03-19 21:26:41

标签: c# .net json image

我正在使用Newtonsoft.Json将我的webservice的输出反序列化为一个对象。 它工作正常,直到我向我的班级(名为Bitmap)添加User属性来持有头像。

webservice将该属性作为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 我发现了根本问题:在Web服务中没有正确地序列化JSON(我没有看到原因)。我认为this是一个有点不同的问题,但没有。我的webservice只是返回一个字符串"System.Drawing.Bitmap"而不是它的base64内容。因此JsonSerializationException

我一直无法解决这个问题,我找到的唯一解决办法是将我的字段变为byte []

3 个答案:

答案 0 :(得分:10)

将该字段读为字符串,

使用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()));
    }
}

答案 1 :(得分:6)

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

[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

答案 2 :(得分:1)

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

之外的所有内容

编辑已编辑的问题

以下是关于如何做到这一点的有趣讨论: JSON.Net Ignore Property during deserialization

我认为你能做的最好就是使用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;
}

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