views:

213

answers:

1

Hello everyone, I tried to serialize a DynamicObject class with BinaryFormatter, but:

  • Output file is too big, not exactly wire-friendly
  • Circular References not handled (stuck while serializing)

Since serializing a DynamicObject means very little by itself, here's the class I tried to serialize:

[Serializable()]
class Entity
    : DynamicObject, ISerializable
{

    IDictionary<string, object> values = new Dictionary<string, object>();

    public Entity()
    {

    }

    protected Entity(SerializationInfo info, StreamingContext ctx)
    {
        string fieldName = string.Empty;
        object fieldValue = null;

        foreach (var field in info)
        {
            fieldName = field.Name;
            fieldValue = field.Value;

            if (string.IsNullOrWhiteSpace(fieldName))
                continue;

            if (fieldValue == null)
                continue;

            this.values.Add(fieldName, fieldValue);
        }

    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        this.values.TryGetValue(binder.Name, out result);

        return true;
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        this.values[binder.Name] = value;

        return true;
    }        

    void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
    {            
        foreach (var kvp in this.values)
        {
            info.AddValue(kvp.Key, kvp.Value);                 
        }
    }

}

(I guess I could have used an ExpandoObject, but that's another story.)

Here's a simple test program:

    static void Main(string[] args)
    {
        BinaryFormatter binFmt = new BinaryFormatter();

        dynamic obj = new Entity();
        dynamic subObj = new Entity();
        dynamic obj2 = null;

        obj.Value = 100;
        obj.Dictionary = new Dictionary<string, int>() { { "la la la", 1000 } };

        subObj.Value = 200;
        subObj.Name = "SubObject";

        obj.Child = subObj;

        using (var stream = new FileStream("test.txt", FileMode.OpenOrCreate))
        {
            binFmt.Serialize(stream, obj);                
        }

        using (var stream = new FileStream("test.txt", FileMode.Open))
        {
            try
            {
                obj2 = binFmt.Deserialize(stream);                    
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }                
        }

        Console.ReadLine();

    }

Putting some breakpoints here and there helped me have a look at obj2 contents and it looks like the original data is correctly deserialized, though with the above shortcomings if you get imaginative and move data around.

I had a look at Marc Gravell's protobuf-net, but I'm not really sure how to use it in such a context (I'm not even sure I picked up the right version from the repository, but hey).

I know it's more code than words, but I don't think I can explain the scenario any better. Please do tell if there's something I can add to make this question clearer.

Any help is much appreciated.

A: 

I am not sure if JSON would be acceptable in your senario, but if it is I have used Json.net (http://json.codeplex.com) to serialize a dynamic types. It works quite well, it is fast, and the output is small in size. While Json.net doesn't return dynamic objects directly, it is very easy to convert the deserialized output of Json.Net to any dynamic type. In the example below I am using ExpandoObject as my dynamic type. The code below is what I have used in the Facebook Graph Toolkit. See this link for the original source: http://facebookgraphtoolkit.codeplex.com/SourceControl/changeset/view/48442#904504

public static dynamic Convert(string s) {
            object obj = Newtonsoft.Json.JsonConvert.DeserializeObject(s);
            if (obj is string) {
                return obj as string;
            } else {
                return ConvertJson((JToken)obj);
            }
    }

    private static dynamic ConvertJson(JToken token) {
        // FROM : http://blog.petegoo.com/archive/2009/10/27/using-json.net-to-eval-json-into-a-dynamic-variable-in.aspx
        // Ideally in the future Json.Net will support dynamic and this can be eliminated.
        if (token is JValue) {
            return ((JValue)token).Value;
        } else if (token is JObject) {
            ExpandoObject expando = new ExpandoObject();
            (from childToken in ((JToken)token) where childToken is JProperty select childToken as JProperty).ToList().ForEach(property => {
                ((IDictionary<string, object>)expando).Add(property.Name, ConvertJson(property.Value));
            });
            return expando;
        } else if (token is JArray) {
            List<ExpandoObject> items = new List<ExpandoObject>();
            foreach (JToken arrayItem in ((JArray)token)) {
                items.Add(ConvertJson(arrayItem));
            }
            return items;
        }
        throw new ArgumentException(string.Format("Unknown token type '{0}'", token.GetType()), "token");
    }
Nathan Totten
@Nathan - thanks, interesting. I'll have a look asap. My only concern is that it won't (allegedly) be as efficient as binary serialization.
Raine