0

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?

MARKAND Bhatt
  • 2,428
  • 10
  • 47
  • 80
  • Do you know when it is return list and when dictionary, or it is random ? – mybirthname Dec 11 '15 at 06:26
  • Its a third party api which returns json. I dont know when it will return list and when dictionary. – MARKAND Bhatt Dec 11 '15 at 06:28
  • 1
    You could write your own [`JavaScriptConverter`](https://msdn.microsoft.com/en-us/library/system.web.script.serialization.javascriptconverter%28v=vs.110%29.aspx) for your class. To say much beyond that, I'd need some specifics of your problem. – dbc Dec 11 '15 at 06:30
  • Alright. Any example will help me. Or is it possible to check the data type before parsing it. – MARKAND Bhatt Dec 11 '15 at 06:32
  • 1
    For instance see [JSON deserialize to class with missing key in json (string could be a single string or list string)](https://stackoverflow.com/questions/34196893/json-deserialize-to-class-with-missing-key-in-json-string-could-be-a-single-str/34212806#34212806). – dbc Dec 11 '15 at 06:32

1 Answers1

1

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
dbc
  • 104,963
  • 20
  • 228
  • 340