views:

146

answers:

1

I would expect a Dictionary object of the form:

var dict = new Dictionary<string,string>()
{
    {"blah", "bob"},
    {"blahagain", "bob"}
};

to serialize into JSON in the form of:

{ "blah": "bob", "blahagain": "bob" }

NOT

[ { "key": "blah", "value": "bob" }, { "key": "blahagain", "value": "bob"}]

What is the reason for what appears to be a monstrosity of a generic attempt at serializing collections?

The DataContractJsonSerializer uses the ISerializable interface to produce this thing. It seems to me as though somebody has taken the XML output from ISerializable and mangled this thing out of it.

Is there a way to override the default serialization used by .Net here? Could I just derive from Dictionary and override the Serialization methods?

Posting to hear of any caveats or suggestions people might have.

A: 

Well, I resolved to sidestep the DataContract serialization for Dictionary objects.

I create an two virtual methods in my root object.

public virtual void prepareForSerialization();
public virtual void postDeserialize();

Then specify string DataMember class properties for each dictionary attribute of my classes. The strings become serialized and the Dictionary is no longer serialized directly.

[DataMember]
public string dictionaryString;
public Dictionary<int, string> dict;

Then, when my code calls serialize, it additionally first calls prepareForSerialization.

public override void prepareForSerialization() { 
    base.prepareForSerialization();
}

public override void postDeSerialize() {
    base.postDeSerialize();
}

Derived classes with Dictionary members will then call my own serializer for the Dictionary.

Note: this is a simple serialization that suits my purposes. YMMV. Javascript ticks credit to another stackoverfow post. Forget which one.

/// <summary>
/// Extension methods needed to implement Javascript dates for C#
/// </summary>
public static class MyExtensions{
    public static double JavascriptTicks(this DateTime dt) {
        DateTime d1 = new DateTime(1970, 1, 1);
        DateTime d2 = dt.ToUniversalTime();
        TimeSpan ts = new TimeSpan(d2.Ticks - d1.Ticks);
        return ts.TotalMilliseconds;
    }        
}
/// <summary>
/// Serialize a single value
/// </summary>
/// <param name="o">An object to serialize</param>
/// <returns>A JSON string of the value</returns>
if (o is string) {
    return string.Format("\"{0}\"", o);
} else if (o is DateTime) {
    return string.Format("new Date({0})", ((DateTime)o).JavascriptTicks()); ;
} else if(o.GetType().IsValueType) {
    return o.ToString();
} else {
    //Here you want a check of the form if is IMySerializer, then call your prepare before
    //using the .Net one.
    DataContractJsonSerializer json = new DataContractJsonSerializer(o.GetType());
    using(MemoryStream ms = new MemoryStream())
    using (StreamReader sr = new StreamReader(ms)) {
        json.WriteObject(ms, o);
        ms.Position = 0;
        return sr.ReadToEnd();
    }
}

/// <summary>
/// Serializes a List object into JSON
/// </summary>
/// <param name="list">The IList interface into the List</param>
/// <returns>A JSON string of the list</returns>
public string SerializeList(IList list) {
    string result = null;
    if (list != null) {
        IEnumerator it = list.GetEnumerator();
        StringBuilder sb = new StringBuilder();
        long len = list.Count;
        sb.Append("[");
        while (it.MoveNext()) {
            if (it.Current is IList) {
                sb.Append(SerializeList((IList)it.Current));
            } else if (it.Current is IDictionary) {
                sb.Append(SerializeDictionary((IDictionary)it.Current));
            } else {
                sb.Append(SerializeValue(it.Current));
            }
            --len;
            if (len > 0) sb.Append(",");
        }
        sb.Append("]");
        result = sb.ToString();
    }
    return result;
}
/// <summary>
/// Returns a stringified key of the object
/// </summary>
/// <param name="o">The key value</param>
/// <returns></returns>
public string SerializeKey(object o) {
    return string.Format("\"{0}\"", o); 
}
/// <summary>
/// Serializes a dictionary into JSON
/// </summary>
/// <param name="dict">The IDictionary interface into the Dictionary</param>
/// <returns>A JSON string of the Dictionary</returns>
public string SerializeDictionary(IDictionary dict) {
    string result = null;
    if (dict != null) {
        IDictionaryEnumerator it = dict.GetEnumerator();
        StringBuilder sb = new StringBuilder();
        long len = dict.Count;
        sb.Append("{");
        while (it.MoveNext()) {
            sb.Append(SerializeKey(it.Key));
            sb.Append(":");
            if (it.Value is IList) {
                sb.Append(SerializeList((IList)it.Value));
            } else if (it.Value is IDictionary) {
                sb.Append(SerializeDictionary((IDictionary)it.Value));
            } else {
                sb.Append(SerializeValue(it.Value));
            }
            --len;
            if (len > 0) sb.Append(",");
        }
        sb.Append("}");
        result = sb.ToString();
    }
    return result;
}   
Jacques
Fixed a bug with value serialization.
Jacques