0

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?

  • Could you show us the code you're using to deserialize? Because that's going to help in determining the problem. You could also look into [deserializing into a dynamic object](https://stackoverflow.com/questions/3142495/deserialize-json-into-c-sharp-dynamic-object). – rickvdbosch Jun 12 '17 at 07:34
  • Added the code that I used for custom serialization for float val and single dimensional array. I need to do the same for float value and multi dimensional array(the default field) – Sangathamilan Ravichandran Jun 12 '17 at 07:45
  • And cant use dynamic object because of restriction on using system.web.helpers :( – Sangathamilan Ravichandran Jun 12 '17 at 07:51
  • What exactly is the problem? Is `"default"` sometimes a single dimensional array? Or sometimes a single item? What cases do you need to handle? And what are you trying to deserialize into? A 2d [jagged array](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/arrays/jagged-arrays)? Or a true [multidimensional array](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/arrays/multidimensional-arrays)? Can you share the actual code that is failing (since that converter is not designed for that specific JSON but for some example JSON from the previous question)? – dbc Jun 12 '17 at 07:58
  • I have to deserialize with two cases. 1. The "default" field is a single float value or Single dimensional array. For which the code from the linked question works fine. Case 2: The default value is a single float value or a Multi dimensional array( as provided in the above json snippet from my json). I need to do the exact same thing which is being done from the linked question but this time for case 2. And my code is the above same code with the names of the classes being different. Is this Understandable? Pls let me know if it isn't.. – Sangathamilan Ravichandran Jun 12 '17 at 08:19
  • And by linked question i mean this one https://stackoverflow.com/questions/44434468/deserialize-a-json-field-with-different-data-types-without-using-newtonsoft-json/44444135?noredirect=1#comment75980175_44444135 – Sangathamilan Ravichandran Jun 12 '17 at 08:20

1 Answers1

0

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.