Serializing dictionaries with JavaScriptSerializer
Asked Answered
L

5

10

Apparently, IDictionary<string,object> is serialized as an array of KeyValuePair objects (e.g., [{Key:"foo", Value:"bar"}, ...]). Is is possible to serialize it as an object instead (e.g., {foo:"bar"})?

Lita answered 20/6, 2011 at 20:17 Comment(1)
yes, dont use JavaScriptSerializer, its utterly crap. Use Newtonsoft Json.NETKrystinakrystle
Y
12

Although I agree that JavaScriptSerializer is a crap and Json.Net is a better option, there is a way in which you can make JavaScriptSerializer serialize the way you want to. You will have to register a converter and override the Serialize method using something like this:

    public class KeyValuePairJsonConverter : JavaScriptConverter
{
    public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
    {
        var instance = Activator.CreateInstance(type);

        foreach (var p in instance.GetType().GetPublicProperties())
        {
            instance.GetType().GetProperty(p.Name).SetValue(instance, dictionary[p.Name], null);
            dictionary.Remove(p.Name);
        }

        foreach (var item in dictionary)
            (instance).Add(item.Key, item.Value);

        return instance;
    }
    public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
    {
        var result = new Dictionary<string, object>();
        var dictionary = obj as IDictionary<string, object>;
        foreach (var item in dictionary)
            result.Add(item.Key, item.Value);
        return result;
    }
    public override IEnumerable<Type> SupportedTypes
    {
        get
        {
            return new ReadOnlyCollection<Type>(new Type[] { typeof(your_type) });
        }
    }
}

JavaScriptSerializer javaScriptSerializer = new JavaScriptSerializer();
javaScriptSerializer.RegisterConverters(new JavaScriptConverter[] { new ExpandoJsonConverter() });
jsonOfTest = javaScriptSerializer.Serialize(test);
// {"x":"xvalue","y":"\/Date(1314108923000)\/"}

Hope this helps!

Yautia answered 31/10, 2012 at 14:28 Comment(1)
I'm trying to use your class but it seems your instance variable of ` Class Object` has no add method.Clot
B
4

No, it is not possible with JavaScriptSerializer. It's possible with Json.NET:

public class Bar
{
    public Bar()
    {
        Foos = new Dictionary<string, string>
        {
            { "foo", "bar" }
        };
    }

    public Dictionary<string, string> Foos { get; set; }
}

and then:

var bar = new Bar();
string json = JsonConvert.SerializeObject(bar, new KeyValuePairConverter());

would produce the desired:

{"Foos":{"foo":"bar"}}
Ballonet answered 20/6, 2011 at 20:34 Comment(4)
I was going that route at first. See my previous question: #6416517. I had a problem deserializing nested dictionaries with Json.NET.Lita
@Daniel, Json.NET is sugar compared to JavaScriptSerializer. So if you have problems with Json.NET I don't know what to say about the alternative :-) As far as your other question is concerned, that's perfectly normal behavior. All you indicated to the serializer is a dictionary of string and object. So you are working with weakly typed dictionaries in your code, what do you expect in return? All you can get of course are a weakly typed JObjects. Use a strongly typed dictionary: Dictionary<string, SomeModel>, there will be a difference.Ballonet
What difference are you referring to? I'm not seeing it.Lita
Sorry, I was just seeing Dictionary vs IDictionary -- got it. Thanks.Lita
R
3

I was able to solve with JavaScriptSerializer with Linq Select:

var dictionary = new Dictionary<int, string>();
var jsonOutput = new JavaScriptSerializer().Serialize(dictionary.Select(x => new { Id = x.Key, DisplayText = x.Value  }));
Renteria answered 14/2, 2017 at 4:28 Comment(2)
This doesn't work in complex cases (i.e. complex hierarchical structures), but it sure solves the problem in the simple case, so it's a solution to consider if you want to avoid adding boilerplate to JavaScriptSerializer or a third party library.Drysalter
For me it did work even in complex cases, just changing to .Serialize(dictionary). Its simpler then JsonConvert, ready to use. I wish I could give +2 to this answerTrommel
L
1

I was able to solve it using JavaScriptSerializer, the trick is to create your own converter. The following code is working code:

public class KeyValuePairJsonConverter : JavaScriptConverter {
    public override object Deserialize(IDictionary<string, object> dictionary
                                        , Type type
                                        , JavaScriptSerializer serializer) {
        throw new InvalidOperationException("Sorry, I do serializations only.");
    }

    public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer) {
        Dictionary<string, object> result = new Dictionary<string, object>();
        Dictionary<string, MyClass> dictionaryInput = obj as Dictionary<string, MyClass>;

        if (dictionaryInput == null) {
            throw new InvalidOperationException("Object must be of Dictionary<string, MyClass> type.");
        }

        foreach (KeyValuePair<string, MyClass> pair in dictionaryInput)
            result.Add(pair.Key, pair.Value);

        return result;
    }

    public override IEnumerable<Type> SupportedTypes {
        get {
            return new ReadOnlyCollection<Type>(new Type[] { typeof(Dictionary<string, MyClass>) });
        }
    }
}

And here's how you use it:

JavaScriptSerializer js = new JavaScriptSerializer();
js.RegisterConverters(new JavaScriptConverter[] { new KeyValuePairJsonConverter() });
Context.Response.Clear();
Context.Response.ContentType = "application/json";
Context.Response.Write(js.Serialize(myObject));
Lett answered 21/2, 2014 at 18:41 Comment(0)
S
1

Here's an I believe improved version from Tomas answer. Works like a charm. We could also add a check for the ScriptIgnore attribute but well, knock yourself out.

BTW, I chose JavaScriptSerializer because in my opinion third party solutions are most of the time: less known, long to install, often forgotten pre-requities and have blur copy-right states that make them risky to distribute in business.

P-S: I didn`t understood why we were trying to deserialize both to the instance and to the instance as a dictionary, so I stripped that part.

public class KeyValuePairJsonConverter : JavaScriptConverter
{
    public override object Deserialize(IDictionary<string, object> deserializedJSObjectDictionary, Type targetType, JavaScriptSerializer javaScriptSerializer)
    {
        Object targetTypeInstance = Activator.CreateInstance(targetType);

        FieldInfo[] targetTypeFields = targetType.GetFields(BindingFlags.Public | BindingFlags.Instance);

        foreach (FieldInfo fieldInfo in targetTypeFields)
            fieldInfo.SetValue(targetTypeInstance, deserializedJSObjectDictionary[fieldInfo.Name]);

        return targetTypeInstance;
    }

    public override IDictionary<string, object> Serialize(Object objectToSerialize, JavaScriptSerializer javaScriptSerializer)
    {
       IDictionary<string, object> serializedObjectDictionary = new Dictionary<string, object>();

       FieldInfo[] objectToSerializeTypeFields = objectToSerialize.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance);

       foreach (FieldInfo fieldInfo in objectToSerializeTypeFields)
           serializedObjectDictionary.Add(fieldInfo.Name, fieldInfo.GetValue(objectToSerialize));

       return serializedObjectDictionary;
    }

    public override IEnumerable<Type> SupportedTypes
    {
        get
        {
            return new ReadOnlyCollection<Type>(new Type[] { typeof(YOURCLASSNAME) });
        }
    }
}
Supercilious answered 22/4, 2014 at 20:43 Comment(1)
Regarding my PS note. Perhaps this was to allow specidying a generic dictionary type rather than a type, but honestly I don`t see the point of specifying a generic dictionary type as this whole thing is about deserializing <String, XYZ> dictionaries. And it's cubersome.Supercilious

© 2022 - 2024 — McMap. All rights reserved.