2013-03-19 4 views
5

Я использую Newtonsoft.Json для десериализации вывода из моего веб-сервиса для объекта. Он работал нормально до тех пор, пока я не добавил свойство Bitmap в мой класс (с именем User), чтобы держать аватар.Newtonsoft.Json deserializing base64 image failed

Вебсервис возвращает это свойство как строку 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, но непонятно, почему.

Любые идеи/временное решение?

EDIT Я знаю, что я могу использовать Convert.FromBase64String действительно получить массив байтов и загрузки растрового изображения из этого. Затем я хотел бы обновить свой вопрос, чтобы узнать о , как можно пропустить или вручную разобрать только это поле. Я бы хотел избежать, чтобы вручную разобрать все JSON. Возможно ли это?

EDIT 2 я узнал, корень проблемы: JSON не будучи правильно сериализовать в WebService (и я не понимаю, почему). Я думал, что this был несколько иной проблемой, но нет. Мой веб-сервис просто возвращает строку "System.Drawing.Bitmap" вместо ее содержимого base64. Следовательно, JsonSerializationException.

Я не смог решить эту проблему, единственным решением, которое я нашел, является превращение моего поля в byte [].

ответ

9

Прочитайте это поле как строку,

новообращенного массив байтов с использованием Convert.FromBase64String и

получить изображение с помощью Bitmap.FromStream(new MemoryStream(bytearray));

EDIT

Вы можете выполнить изображение сериализации/десериализации с справка изготовителя преобразователь

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())); 
    } 
} 
+0

Пожалуйста, обратитесь к моему редактируемого вопрос. Есть ли способ сделать 'JsonConvert.DeserializeObject (json)' пропускать только поле? Это внутри общего метода, поэтому я хотел бы избежать полного ручного анализа ... – Joel

+0

@Joel См. Мое редактирование ..... Нет необходимости в регулярном выражении или ручном анализе строки json. – I4V

+0

С помощью этого кода мне приходится вручную сериализовать свой объект в веб-сервисе, не так ли? – Joel

1

Я думаю, что десериализации от Base64 к System.Drawing.Bitmap не поддерживается.Может быть, вы можете попробовать десериализации все excepts на Avatar свойство

EDIT для редактируемого ВОПРОСА

Вот интересная дискуссия о том, как сделать это: JSON.Net Ignore Property during deserialization

Я думаю, что самое лучшее, что вы можете сделать будет использовать Regex, чтобы удалить свойство из строки JSON:

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

, а затем десериализации тыс is newJsonString без свойства Avatar.

Позже вы можете разобрать исходную строку 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; 
} 

Вы можете улучшить регулярные выражения или метод, но это основная идея.

+0

"вы можете попробовать десериализовать все, кроме свойства Avatar ..." Есть ли способ сделать это без ручного анализа всех элементов? – Joel

+1

a) Для кодировки Base64 требуется 64 символа, но я вижу только 62 в этом регулярном выражении 'A-Za-z0-9' b) Попытка разобрать json с регулярным выражением не является приятной. c) "* Я думаю, что десериализация из Base64 в System.Drawing.Bitmap не поддерживается. *" неверно. d) вы можете объявить поле 'avatar' как строку и построить изображение в C# без регулярного выражения. –

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