I'm using Newtonsoft.Json to deserialize the output from my webservice to an object. It worked fine until I added a Bitmap
property to my class (named User
) to hold an avatar.
The webservice is returning that propert as a Base64 string, which is as expected. The problem is when I try to convert back the JSON from the WS to a List<User>
, a JsonSerializationException
is thrown in this block of code:
// 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!
});
Output from exception is:
Error converting value "System.Drawing.Bitmap" to type 'System.Drawing.Bitmap'. Path '[2].Avatar
and looking at the inner exception:
{"Could not cast or convert from System.String to System.Drawing.Bitmap."}
It's clear that it fails to parse the Base64 string, but it's not clear why.
Any ideas/workaround?
EDIT I know I can use Convert.FromBase64String
do get a byte array and load a bitmap from that. Then I'd like to update my question to ask about how can I skip or manually parse only that field. I would like to avoid, having to manually parse all the JSON. Is this even possible?
EDIT 2 I found out the root problem: JSON is not being correctly serialized in webservice (and I fail to see why). I thought that this was a somewhat different issue, but no. My webservice is simply returning a string "System.Drawing.Bitmap"
instead of its base64 content. Hence the JsonSerializationException
.
I have been unable to solve that issue, the only solution I've found is to turn my field into a byte []
.
Read that field as string,
convert to byte array using Convert.FromBase64String
and
get the image using Bitmap.FromStream(new MemoryStream(bytearray));
EDIT
You can perform image serialization/deserialization with the help of a custom converter
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());
This is the 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()));
}
}
This is my solution, I used the annotation
[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
I think that the Deserializing from Base64
to System.Drawing.Bitmap
is not supported. May be you can try deserializing everything excepts the Avatar
property
EDIT FOR EDITED QUESTION
Here is an interesting discussion on how to do that: JSON.Net Ignore Property during deserialization
I think the best that you can do is use Regex
to remove the property from the json string:
var newJsonString = Regex.Replace(jsonString,
"(\\,)* \"Avatar\": \"[A-Za-z0-9]+\"",
String.Empty);
and then deserialize this newJsonString
without the Avatar property.
Later you can parse the original json string to get the base64
and build the 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;
}
You could improve the regular expressions or the method, but that's the basic idea.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.