0

When I Deserialize this object everything works except for Images? The only way I have figured out how to get the images out is to use the method 'GetImages()'. I would like to not have to use this a method where I return a Dictionary Key results. I've tried structuring the object so many different way, but have yet to successfully find the answer.

Here is the object.

public class Item
{
    public int itemId { get; set; }
    public double systemSku { get; set; }
    public int itemECommerceID { get; set; }

    public Dictionary<string, List<Image>> Images { get; set; }
    public Category Category { get; set; }

    public List<Image> GetImages()
    {
        return this.Images["Image"];
    }
}

Image Class:

public class Image
{
    public int imageID { get; set; }
    public string description { get; set; }
    public string filename { get; set; }
    public int ordering { get; set; }
    public string publicID { get; set; }
    public string baseImageURL { get; set; }
    public int itemID { get; set; }
    public int itemMatrixID { get; set; }

    public string imageUrl
    {
        get
        {
            return string.Format("{0}{1}.jpg", baseImageURL, publicID);
        }
    }
}

Excerpt of serialization code:

var stream = await response.Content.ReadAsStreamAsync();
StreamReader reader = new StreamReader(stream);
JavaScriptSerializer js = new JavaScriptSerializer();
var tmpObj = js.Deserialize<dynamic>(reader.ReadToEnd());
TResult obj = js.Deserialize<TResult>(js.Serialize(tmpObj[key]));
  • http://stackoverflow.com/questions/1299071/serializing-net-dictionary – tdbeckett Dec 12 '14 at 19:20
  • How are you deserializing it? – Andrew Whitaker Dec 12 '14 at 19:25
  • If you are only getting the list of images using the method you have written GetImages, I'm not sure what you are getting from using the Dictionary. Why not just make the property Images a List? I believe a list of Images will would Serialized and Deserialize fine. – tdbeckett Dec 12 '14 at 19:27
  • @AndrewWhitaker updated post with serialization code. –  Dec 12 '14 at 19:27
  • I'm not able to reproduce this problem if I fully deserialize the JSON via `js.Deserialize(reader.ReadToEnd())`. How are you calling the method above? What are you passing for `TResult`? – dbc Dec 12 '14 at 21:41

2 Answers2

1

JSON serialization does not on it's own handle complex typed (non string, int etc...) dictionaries, you would have to create your own Dictionary Serialization mechanism or do what you are doing via List<>.

I ran across this same issue when trying to make a more efficient serializer/deserializer for WCF, while JSON will be more compact it is lacking in complicated object graphs.

T McKeown
  • 12,971
  • 1
  • 25
  • 32
  • `JavaScriptSerializer` is perfectly happy to serialize a dictionary, it does so in the same format as JSON.Net, turning the key/value pairs into name/value pairs. For instance, if I have a `Dictionary>`, `JavaScriptSerializer` will serialize/deserialize its JSON as follows: `{"first":[1],"second":[2,22]}`. – dbc Dec 12 '14 at 20:16
  • yeah, those are simple types. more complicated object graphs do not serialize so nicely – T McKeown Dec 12 '14 at 20:39
  • I think the big problem here is going to be serializing your Image values. You will need to convert them to Byte[] values. – Mike Burdick Dec 12 '14 at 20:54
  • My 'Image' class is not an image itself, it is just another class with properties and a URL to an image. –  Dec 12 '14 at 21:19
  • @KyleRogers - that would have been helpful to know. – dbc Dec 12 '14 at 21:20
1

Purely as a matter of serialization/deserialization, I was able to serialize and deserialize your Dictionary<string, List<Image>> by using the following JavaScriptConverter:

public class JavaScriptImageConverter : JavaScriptConverter
{
    readonly List<Type> Types = new List<Type>();

    const string base64key = "Base64Image";

    public JavaScriptImageConverter() : base()
    {
        Types.Add(typeof(Bitmap)); // Add additional types if required?
        Types.Add(typeof(Image));
    }

    static string ToBase64String(System.Drawing.Image imageIn)
    {
        if (imageIn == null)
            return null;
        ImageConverter converter = new ImageConverter();
        return Convert.ToBase64String((byte[])converter.ConvertTo(imageIn, typeof(byte[])));
    }

    public static Image FromBase64String(string imageString)
    {
        if (string.IsNullOrEmpty(imageString))
            return null;
        ImageConverter converter = new ImageConverter();
        return (Image)converter.ConvertFrom(Convert.FromBase64String(imageString));
    }

    public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
    {
        if (!typeof(Image).IsAssignableFrom(type))
            return null;
        object obj;
        if (!dictionary.TryGetValue(base64key, out obj))
            return null;
        var str = obj as string;
        if (str == null)
            return null;
        return FromBase64String(str);
    }

    public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
    {
        var image = (Image)obj;
        var serialized = new Dictionary<string, object>();
        serialized[base64key] = ToBase64String(image);
        return serialized;
    }

    public override IEnumerable<Type> SupportedTypes
    {
        get {
            return Types;
        }
    }
}

And then use it like:

            var js = new JavaScriptSerializer();

            js.RegisterConverters(new JavaScriptConverter[] { new JavaScriptImageConverter() });
            js.MaxJsonLength = int.MaxValue / 8; // Because my test string turned out to be VERY LONG.

            var imageJson = js.Serialize(item);

            using (var itemBack = js.Deserialize<Item>(imageJson))
            {
                var ok1 = itemBack.Images.SelectMany(p => p.Value).Select(i => i.Width).SequenceEqual(item.Images.SelectMany(p => p.Value).Select(i => i.Width));
                Debug.Assert(ok1); // No assert.
                var ok2 = itemBack.Images.SelectMany(p => p.Value).Select(i => i.Height).SequenceEqual(item.Images.SelectMany(p => p.Value).Select(i => i.Height));
                Debug.Assert(ok2); // No assert.
            }

Note I got much better serialization/deserialization performance using a base64 string than by using a byte [] array directly.

(Maybe this way of serializing images is too nonstandard for your tastes?)

dbc
  • 104,963
  • 20
  • 228
  • 340
  • This is the right approach if you are trying to Serialize System.Drawing.Image types. It looks like the op is not serializing System.Drawing.Image but a class that might be JSon serializable without having to implement a JavaScriptSerializaer. – Mike Burdick Dec 12 '14 at 21:26
  • @MikeBurdick - yes, the question was updated with that information after I posted this answer. – dbc Dec 12 '14 at 21:36