I'm using JavascriptSerializer with custom JavascriptConverter like this:
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
SomeObjectModel TheObject = obj as SomeObjectModel;
Dictionary<string, object> OutputJson = new Dictionary<string, object>();
OutputJson.Add("SomeKey", SomeObjectModel.SomeProperty);
return OutputJson;
}
Now I want to be able to dynamically change SomeKey at runtime so I thought of creating a dictionary of keys, passing this dictionary to the serializer, and doing something like this:
OutputJson.Add(TheJsonDictionary.SomeKey, SomeObjectModel.SomeProperty);
However, when I add a parameter to the function call like this:
public override IDictionary<string, object> Serialize(Dictionary<string, string> TheJsonDictionary, object obj, JavaScriptSerializer serializer)
I get an error message at compilation. Now I know why I'm getting this error (the abstract method is defined with 2 parameters and I'm passing 3) and I'm wondering how to go around this so that I can pass in a dictionary to encode the keys.
Thanks.
I guess the only workaround would be to subclass the JavaScriptSerializer and reimplement the Serialize method to accept an arbitrary TheJsonDictionary dictionary:
class CustomJavaScriptSerializer : JavaScriptSerializer
{
public Dictionary<string, string> TheJsonDictionary { get; private set; }
public string Serialize(object obj, Dictionary<string, string> TheJsonDictionary = null)
{
this.TheJsonDictionary = TheJsonDictionary;
return base.Serialize(obj);
}
}
Now in your custom converter you can cast the serializer object back to CustomJavaScriptSerializer and get the custom diectionary of keys:
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
var TheJsonDictionary = (serializer as CustomJavaScriptSerializer).TheJsonDictionary;
// rest of method
}
As I understood, this is what you need:
class Program
{
static void Main(string[] args)
{
var dict = new Dictionary<string, string>();
//dict.Add("SomeKey", "SomeProperty1");
dict.Add("SomeKey", "SomeProperty2");
var myConverter = new MyConverter();
var serialized = myConverter.Serialize(dict, new SomeObjectModel() { SomeProperty1 = 1, SomeProperty2 = 2 }, new MySerializer());
}
class MySerializer : JavaScriptSerializer
{ }
class MyConverter : JavaScriptConverter
{
Dictionary<string, string> _theJsonDictionary;
public IDictionary<string, object> Serialize(Dictionary<string, string> TheJsonDictionary, object obj, JavaScriptSerializer serializer)
{
_theJsonDictionary = TheJsonDictionary;
return Serialize(obj, serializer);
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
SomeObjectModel TheObject = obj as SomeObjectModel;
Dictionary<string, object> OutputJson = new Dictionary<string, object>();
foreach (var item in _theJsonDictionary.Keys)
{
OutputJson.Add(item, TheObject.GetType().GetProperty(_theJsonDictionary[item]).GetValue(TheObject));
}
return OutputJson;
}
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
return new object();
}
public override IEnumerable<Type> SupportedTypes
{
get { return new[] { typeof(object) }.AsEnumerable(); }
}
}
class SomeObjectModel
{
public int SomeProperty1 { get; set; }
public int SomeProperty2 { get; set; }
}
}
Related
Hi I had an issue while deserializing a json file which can have different data type( float and float array). I got a suggestion to use custom converter from Deserialize a json field with different data types without using Newtonsoft json but with System.Web.Script.Serialization.JavaScriptSerializer and it works fine for one dimensional array. I am currently being given jsons with two dimensional array and this converter couldn't handle the same. Below is a snippet from my json
{
"name" : "foo",
"value" : 457,
"comment" : "bla bla bla",
"data" : [
{
"name" : "bar",
"max" : 200,
"default" : [
[7,4],[2,2],[7,4],[1,1],[2,3],[3,1],[7,9]
]
}
]
}
Where the default can be sometimes primitive like
"default" : 3.56
Edit : Code used for custom serialization
class RootObjectConverter : CustomPropertiesConverter<RootObject>
{
const string ValuesName = "values";
protected override IEnumerable<string> CustomProperties
{
get { return new[] { ValuesName }; }
}
protected override void DeserializeCustomProperties(Dictionary<string, object> customDictionary, RootObject obj, JavaScriptSerializer serializer)
{
object itemCost;
if (customDictionary.TryGetValue(ValuesName, out itemCost) && itemCost != null)
obj.Values = serializer.FromSingleOrArray<float>(itemCost).ToArray();
}
protected override void SerializeCustomProperties(RootObject obj, Dictionary<string, object> dict, JavaScriptSerializer serializer)
{
obj.Values.ToSingleOrArray(dict, ValuesName);
}
}
public abstract class CustomPropertiesConverter<T> : JavaScriptConverter
{
protected abstract IEnumerable<string> CustomProperties { get; }
protected abstract void DeserializeCustomProperties(Dictionary<string, object> customDictionary, T obj, JavaScriptSerializer serializer);
protected abstract void SerializeCustomProperties(T obj, Dictionary<string, object> dict, JavaScriptSerializer serializer);
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
// Detach custom properties
var customDictionary = new Dictionary<string, object>();
foreach (var key in CustomProperties)
{
object value;
if (dictionary.TryRemoveInvariant(key, out value))
customDictionary.Add(key, value);
}
// Deserialize and populate all members other than "values"
var obj = new JavaScriptSerializer().ConvertToType<T>(dictionary);
// Populate custom properties
DeserializeCustomProperties(customDictionary, obj, serializer);
return obj;
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
// Generate a default serialization. Is there an easier way to do this?
var defaultSerializer = new JavaScriptSerializer();
var dict = defaultSerializer.Deserialize<Dictionary<string, object>>(defaultSerializer.Serialize(obj));
// Remove default serializations of custom properties, if present
foreach (var key in CustomProperties)
{
dict.RemoveInvariant(key);
}
// Add custom properties
SerializeCustomProperties((T)obj, dict, serializer);
return dict;
}
public override IEnumerable<Type> SupportedTypes
{
get { return new[] { typeof(T) }; }
}
}
public static class JavaScriptSerializerObjectExtensions
{
public static void ReplaceInvariant<T>(this IDictionary<string, T> dictionary, string key, T value)
{
RemoveInvariant(dictionary, key);
dictionary.Add(key, value);
}
public static bool TryRemoveInvariant<T>(this IDictionary<string, T> dictionary, string key, out T value)
{
if (dictionary == null)
throw new ArgumentNullException();
var keys = dictionary.Keys.Where(k => string.Equals(k, key, StringComparison.OrdinalIgnoreCase)).ToArray();
if (keys.Length == 0)
{
value = default(T);
return false;
}
else if (keys.Length == 1)
{
value = dictionary[keys[0]];
dictionary.Remove(keys[0]);
return true;
}
else
{
throw new ArgumentException(string.Format("Duplicate keys found: {0}", String.Join(",", keys)));
}
}
public static void RemoveInvariant<T>(this IDictionary<string, T> dictionary, string key)
{
if (dictionary == null)
throw new ArgumentNullException();
foreach (var actualKey in dictionary.Keys.Where(k => string.Equals(k, key, StringComparison.OrdinalIgnoreCase)).ToArray())
dictionary.Remove(actualKey);
}
public static void ToSingleOrArray<T>(this ICollection<T> list, IDictionary<string, object> dictionary, string key)
{
if (dictionary == null)
throw new ArgumentNullException();
if (list == null || list.Count == 0)
dictionary.RemoveInvariant(key);
else if (list.Count == 1)
dictionary.ReplaceInvariant(key, list.First());
else
dictionary.ReplaceInvariant(key, list.ToArray());
}
public static List<T> FromSingleOrArray<T>(this JavaScriptSerializer serializer, object value)
{
if (value == null)
return null;
if (value.IsJsonArray())
{
return value.AsJsonArray().Select(i => serializer.ConvertToType<T>(i)).ToList();
}
else
{
return new List<T> { serializer.ConvertToType<T>(value) };
}
}
public static bool IsJsonArray(this object obj)
{
if (obj is string || obj is IDictionary)
return false;
return obj is IEnumerable;
}
public static IEnumerable<object> AsJsonArray(this object obj)
{
return (obj as IEnumerable).Cast<object>();
}
}
and used it like
var serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new[] { new RootObjectConverter() });
var root = serializer.Deserialize<RootObject>(json);
Can anyone help me on how to proceed on this?
I fixed the issue by using a Dictionary instead of a class object for parsing different data type fields and accessing the respective property with the key value. Thanks Everyone for your suggestions.
I'm very new with JSON, please help!
I am trying to serialise a List<KeyValuePair<string, string>> as JSON
Currently:
[{"Key":"MyKey 1","Value":"MyValue 1"},{"Key":"MyKey 2","Value":"MyValue 2"}]
Expected:
[{"MyKey 1":"MyValue 1"},{"MyKey 2":"MyValue 2"}]
I referred to some examples from this and this.
This is my KeyValuePairJsonConverter : JsonConverter
public class KeyValuePairJsonConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
List<KeyValuePair<object, object>> list = value as List<KeyValuePair<object, object>>;
writer.WriteStartArray();
foreach (var item in list)
{
writer.WriteStartObject();
writer.WritePropertyName(item.Key.ToString());
writer.WriteValue(item.Value.ToString());
writer.WriteEndObject();
}
writer.WriteEndArray();
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(List<KeyValuePair<object, object>>);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jsonObject = JObject.Load(reader);
var target = Create(objectType, jsonObject);
serializer.Populate(jsonObject.CreateReader(), target);
return target;
}
private object Create(Type objectType, JObject jsonObject)
{
if (FieldExists("Key", jsonObject))
{
return jsonObject["Key"].ToString();
}
if (FieldExists("Value", jsonObject))
{
return jsonObject["Value"].ToString();
}
return null;
}
private bool FieldExists(string fieldName, JObject jsonObject)
{
return jsonObject[fieldName] != null;
}
}
I am calling it from a WebService method like this
List<KeyValuePair<string, string>> valuesList = new List<KeyValuePair<string, string>>();
Dictionary<string, string> valuesDict = SomeDictionaryMethod();
foreach(KeyValuePair<string, string> keyValue in valuesDict)
{
valuesList.Add(keyValue);
}
JsonSerializerSettings jsonSettings = new JsonSerializerSettings { Converters = new [] {new KeyValuePairJsonConverter()} };
string valuesJson = JsonConvert.SerializeObject(valuesList, jsonSettings);
You can use Newtonsoft and dictionary:
var dict = new Dictionary<int, string>();
dict.Add(1, "one");
dict.Add(2, "two");
var output = Newtonsoft.Json.JsonConvert.SerializeObject(dict);
The output is :
{"1":"one","2":"two"}
Edit
Thanks to #Sergey Berezovskiy for the information.
You are currently using Newtonsoft, so just change your List<KeyValuePair<object, object>> to Dictionary<object,object> and use the serialize and deserialize method from the package.
So I didn't want to use anything but native c# to solve a similar issue and for reference this was using .net 4, jquery 3.2.1 and backbone 1.2.0.
My issues was that the List<KeyValuePair<...>> would process out of the controller into a backbone model but when I saved that model the controller could not bind List.
public class SomeModel {
List<KeyValuePair<int, String>> SomeList { get; set; }
}
[HttpGet]
SomeControllerMethod() {
SomeModel someModel = new SomeModel();
someModel.SomeList = GetListSortedAlphabetically();
return this.Json(someModel, JsonBehavior.AllowGet);
}
network capture:
"SomeList":[{"Key":13,"Value":"aaab"},{"Key":248,"Value":"aaac"}]
But even though this set SomeList properly in the backing model.js trying to save the model without any changes to it would cause the binding SomeModel object to have the same length as the parameters in the request body but all the keys and values were null:
[HttpPut]
SomeControllerMethod([FromBody] SomeModel){
SomeModel.SomeList; // Count = 2, all keys and values null.
}
The only things I could find is that KeyValuePair is a structure and not something that can be instantiated in this manner. What I ended up doing is the following:
Add a Model wrapper somewhere that contains key, value fields:
public class KeyValuePairWrapper {
public int Key { get; set; }
public String Value { get; set; }
//default constructor will be required for binding, the Web.MVC binder will invoke this and set the Key and Value accordingly.
public KeyValuePairWrapper() { }
//a convenience method which allows you to set the values while sorting
public KeyValuePairWrapper(int key, String value)
{
Key = key;
Value = value;
}
}
Set up your binding class model to accept your custom wrapper object.
public class SomeModel
{
public List<KeyValuePairWrapper> KeyValuePairList{ get; set };
}
Get some json data out of a controller
[HttpGet]
SomeControllerMethod() {
SomeModel someModel = new SomeModel();
someModel.KeyValuePairList = GetListSortedAlphabetically();
return this.Json(someModel, JsonBehavior.AllowGet);
}
Do something at a later time, maybe model.save(null, ...) is invoked
[HttpPut]
SomeControllerMethod([FromBody] SomeModel){
SomeModel.KeyValuePairList ; // Count = 2, all keys and values are correct.
}
I get a json string which have few data uniformity issue.
For example one field in json string returns a list of string while the same field in other json string returns a dictionary(key, value pairs).
My class which holds the parsed json values have property for the field as List.
Because of this data uniformity problem, the json string is not parsed properly.
Following is my code to parse the json string
JavaScriptSerializer serializer = new JavaScriptSerializer();
myClass mc = serializer.Deserialize<myClass>(jsonString);
IS there any way with which i can write custom code to parse the json string and map it to myClass?
You don't give a concrete example of what you are trying to accomplish, which means we need to make up an example ourselves. Consider the following class:
public class myClass
{
public Dictionary<string, string> data { get; set; }
}
And consider the following two JSON strings:
{"data": ["zero", 1, "two"]}
{"data": {"0": "zero", "1":1, "2":"two"}}
It seems like you might like to parse these identically, with the array being converted to a Dictionary<string, string> whose keys are array indices. This can be accomplished with the following JavaScriptConverter:
public class myClassConverter : JavaScriptConverter
{
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
var myClass = new myClass();
object data;
if (dictionary.TryGetValue("data", out data))
{
if (data.IsJsonArray())
{
myClass.data = data.AsJsonArray()
.Select((o, i) => new KeyValuePair<int, object>(i, o))
.ToDictionary(p => p.Key.ToString(NumberFormatInfo.InvariantInfo), p => serializer.ConvertToType<string>(p.Value));
}
else if (data.IsJsonObject())
{
myClass.data = data.AsJsonObject()
.ToDictionary(p => p.Key, p => serializer.ConvertToType<string>(p.Value));
}
}
return myClass;
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
throw new NotImplementedException();
}
public override IEnumerable<Type> SupportedTypes
{
get { return new [] { typeof(myClass) }; }
}
}
public static class JavaScriptSerializerObjectExtensions
{
public static bool IsJsonArray(this object obj)
{
if (obj is string || obj.IsJsonObject())
return false;
return obj is IEnumerable;
}
public static IEnumerable<object> AsJsonArray(this object obj)
{
return (obj as IEnumerable).Cast<object>();
}
public static bool IsJsonObject(this object obj)
{
return obj is IDictionary<string, object>;
}
public static IDictionary<string, object> AsJsonObject(this object obj)
{
return obj as IDictionary<string, object>;
}
}
The IDictionary<string, object> passed to Deserialize() corresponds to the key/value pairs in the JSON object being converted. For a particular key ("data" in this case) the object value will be an IDictionary<string, object> if the value is, in turn, a JSON object, and an IEnumerable (specifically an ArrayList) if the value is a JSON array. By testing the value against the appropriate type, a conversion can be made.
The converter only does deserialization. Use it like so:
var jsonString1 = #"{""data"": [""zero"", 1, ""two""]}";
var jsonString2 = #"{""data"": {""0"": ""zero"", ""1"":1, ""2"":""two""}}";
var deserializer = new JavaScriptSerializer();
deserializer.RegisterConverters(new JavaScriptConverter[] { new myClassConverter() });
var newJson1 = new JavaScriptSerializer().Serialize(deserializer.Deserialize<myClass>(jsonString1));
var newJson2 = new JavaScriptSerializer().Serialize(deserializer.Deserialize<myClass>(jsonString2));
Console.WriteLine(newJson1); // Prints {"data":{"0":"zero","1":"1","2":"two"}}
Console.WriteLine(newJson2); // Prints {"data":{"0":"zero","1":"1","2":"two"}}
Debug.Assert(newJson1 == newJson2); // No assert
The below illustration represents building Object of type TestClass2 using object of type TestClass1 with the help of Serialization/Deserialization.
TestClass1 and TestClass2 have the same structure except one of the members is string in TestClass1 but long in TestClass2.
public class TestClass1
{
public string strlong;
}
public class TestClass2
{
public long strlong;
}
TestClass1 objT1 = new TestClass1();
objT1.strlong = "20134567";
TestClass2 objT2;
JavaScriptSerializer serializer = new JavaScriptSerializer();
string JSON1 = serializer.Serialize(objT1);
objT2 = serializer.Deserialize<TestClass2>(JSON1);
After the operation, objT2 will have the values of objT1 but strlong will now be long as opposed to string.
The problem is, if the strlong value in objT1 is an empty string --> "", the deserialization fails with an exception "" is not a valid value for Int64.
If strlong is non empty string with just numeric characters, the current deserialization works. But I do not know the workaround when something like empty string appears.
For now, lets assume that
strlong will be in the range of long
Will just be a sequence of numeric characters i.e. it will not have . or , or / or any type of other characters
Have access to only objects for serialization and I cannot make modifications to TestClass1 or TestClass2.
If there is a simple way (or not) of Creating objects of one class using objects of another class, please mention that in the comments.
EDIT-Extending the logic
To extend the logic of solution given in the Answer below to Classes containing members of type other classes, I have used the serialization solution given below to the member items as well. In other words, if classes contain members of other classes, is there a better way of handling the deeper levels than the code below?
// **Item1 :**
// These are the subclasses and classes
// whose objects I am trying to serialize
// and deserialize from one type to another
public class SubClass1
{
public string toomuch;
public int number = 30;
}
public class SubClass2
{
public long toomuch;
public int number;
}
public class TestClass1
{
public string strlong;
public SubClass1 item2;
}
public class TestClass2
{
public long strlong;
public SubClass2 item2;
}
// **Item2 :**
// Solution from StackOverflow for serialization of
// empty string
public class TestClass1Converter : JavaScriptConverter
{
public override IEnumerable<Type> SupportedTypes
{
get { return new Type[] { typeof(TestClass1) }; }
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
var data = obj as TestClass1;
var dic = new Dictionary<string, object>();
if (data == null)
{
return dic;
}
long val = 0;
long.TryParse(data.strlong, out val);
dic.Add("strlong", val);
// **Item3 :**
// trying to serialize and deserialize item2 which is of type SubClass1
// which might also have empty string
/*******************/
JavaScriptSerializer subClassSerializer = new JavaScriptSerializer();
subClassSerializer.RegisterConverters(new[] { new SubClass1Converter() });
string JSONstr = subClassSerializer.Serialize(data.item2);
dic.Add("item2", subClassSerializer.Deserialize<SubClass2>(JSONstr));
/*******************/
return dic;
}
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
throw new NotImplementedException();
}
}
// **Item4 :**
// Serialization for subclass
public class SubClass1Converter : JavaScriptConverter
{
public override IEnumerable<Type> SupportedTypes
{
get { return new Type[] { typeof(SubClass1) }; }
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
var data = obj as SubClass1;
var dic = new Dictionary<string, object>();
if (data == null)
{
return dic;
}
long val = 0;
long.TryParse(data.toomuch, out val);
dic.Add("toomuch", val);
dic.Add("number", data.number);
return dic;
}
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
throw new NotImplementedException();
}
}
class Program
{
static void Main(string[] args)
{
TestClass1 objT1 = new TestClass1();
objT1.strlong = "";
SubClass1 objSub = new SubClass1();
objSub.toomuch = "";
objT1.item2 = objSub;
TestClass2 objT2;
JavaScriptSerializer serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new[] { new TestClass1Converter() });
string JSON1 = serializer.Serialize(objT1);
objT2 = serializer.Deserialize<TestClass2>(JSON1);
}
}
You should declare your TestClass2.strlong as nullable.
public class TestClass2
{
public long? strlong;
}
Now you can have null in case when the TestClass1.strlong is empty string or null.
Here is UPDATE in case that you haven't access to modify the classes.
You should add to the serializer the converter via RegisterConverters to customize conversion. Here is the example:
public class TestClass1Converter : JavaScriptConverter
{
public override IEnumerable<Type> SupportedTypes
{
get { return new Type[] { typeof(TestClass1)}; }
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
var data = obj as TestClass1;
var dic = new Dictionary<string, object>();
if(data == null)
{
return dic;
}
long val = 0;
long.TryParse(data.strlong, out val);
dic.Add("strlong", val);
return dic;
}
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
throw new NotImplementedException();
}
}
This converter will serialize strlong to 0 in case when it is not convertible to long. You can use it in this way:
TestClass1 objT1 = new TestClass1();
objT1.strlong = "444";
TestClass2 objT2;
JavaScriptSerializer serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new [] {new TestClass1Converter()});
string JSON1 = serializer.Serialize(objT1);
objT2 = serializer.Deserialize<TestClass2>(JSON1);
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"})?
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!
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"}}
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 }));
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));
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) });
}
}
}