【问题标题】:Newtonsoft.Json deserializing base64 image failsNewtonsoft.Json 反序列化 base64 图像失败
【发布时间】:2013-03-19 21:26:41
【问题描述】:

我正在使用 Newtonsoft.Json 将我的 web 服务的输出反序列化为一个对象。 在我向我的班级(命名为User)添加一个Bitmapproperty 来保存头像之前,它工作得很好。

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 获取一个字节数组并从中加载位图。然后我想更新我的问题,询问 如何 我可以跳过或手动解析 only 该字段。 我想避免必须手动解析所有 JSON。 这甚至可能吗?

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

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

【问题讨论】:

    标签: c# .net json image


    【解决方案1】:

    将该字段读取为字符串,

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

    【讨论】:

    • 请参考我编辑的问题。有没有办法让JsonConvert.DeserializeObject&lt;T&gt;(json) 只跳过一个字段?这是一个通用方法,所以我想避免一个完整的手动解析......
    • 有了这段代码,我必须在网络服务中手动序列化我的对象,对吧?
    • @Joel 没有。如果您的字段/属性的类型是Bitmap,则此代码将base64 编码的字符串反序列化为位图。见AClass的定义
    • 查看我的更新。您的解决方案非常有前途,但由于根本问题已经改变,我无法对其进行测试。如果我解决了这个问题,我一定会测试你的解决方案。
    • @Joel 就用JsonConvert.SerializeObject(yourObject,new ImageConverter());
    【解决方案2】:

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

    [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
    

    【讨论】:

      【解决方案3】:

      我认为不支持从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;
      }
      

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

      【讨论】:

      • “您可以尝试反序列化除 Avatar 属性之外的所有内容...” 有没有办法做到这一点而无需手动解析所有元素?
      • a) Base64 编码需要 64 个字符,但我在这个正则表达式中只看到 62 个字符 A-Za-z0-9 b) 尝试使用正则表达式解析 json 并不好。 c) “我认为不支持从 Base64 到 System.Drawing.Bitmap 的反序列化。”不正确。 d) 您可以将avatar 字段声明为字符串,并在不使用正则表达式的c# 中构造图像。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-02
      • 2020-06-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多