4

I have a class I use for configuration that holds a 3 tuple of all integers.

public class Configuration
{
    public List<Tuple<int, int, int>> MyThreeTuple { get; set; }

    public Configuration()
    {
        MyThreeTuple = new List<Tuple<int, int, int>>();
        MyThreeTuple.Add(new Tuple<int, int, int>(-100, 20, 501));
        MyThreeTuple.Add(new Tuple<int, int, int>(100, 20, 864));
        MyThreeTuple.Add(new Tuple<int, int, int>(500, 20, 1286));
    }
}

This class is serialized into Json with System.Web.Script.Serialization as such

JavaScriptSerializer jsonSerializer = new JavaScriptSerializer();
String configJson = jsonSerializer.Serialize(config);
File.WriteAllText(configPath, configJson);

and then deserialized when the application is launched

String configJson = File.ReadAllText(configPath);
JavaScriptSerializer jsonSerializer = new JavaScriptSerializer(); 
object jsonObject = jsonSerializer.Deserialize(configJson, typeof(ApplicationConfiguration));
appConfig = (Configuration)jsonObject;

My issue is that whenever I go to deserialize the Json an exception is thrown

System.MissingMethodException in System.Web.Extensions.dll 

With the message

No parameterless constructor defined for type of `System.Tuple`3 ....

This is what the produced Json for the tuple looks like

"MyThreeTuple":
 [
    {"Item1":-100,"Item2":20,"Item3":501},
    {"Item1":100,"Item2":20,"Item3":864},
    {"Item1":500,"Item2":20,"Item3":1286}
 ]

Any idea how to solve this problem?

EDIT:

As other have suggested I tried out JSon.NET and it seems to work to deserialize the json. Though I have found an odd quirk.

So the constructor for my Configuration class fills in default values for the configuration. Specifically what is given in the code above. I have found that I can serialize the configuration with different values, but when deserialized, the default values are also loaded to the List<Tuple<>>.

I assume the Json.Net gets around the issue of Tuple not having a parameterless constructor by first instantiating Configuration and then setting its values.

It seems to be instantiating the class, then List.Adding the Tuples it finds in the json to the list. Instead of clearing the list then adding what it finds.

Is there an option in JSon.Net to solve this issue?

BRAHIM Kamel
  • 13,492
  • 1
  • 36
  • 47
KDecker
  • 6,928
  • 8
  • 40
  • 81
  • Systen.Tuple doesn't have parameterless constructor at all. – MajkeloDev Oct 30 '15 at 13:22
  • http://stackoverflow.com/questions/13739348/why-i-could-not-serialize-a-tuple-in-c read this Question and answers – MajkeloDev Oct 30 '15 at 13:24
  • 1
    If you are willing to use JSON.net, it [supports Tuple serialization](http://stackoverflow.com/questions/16101115/how-does-a-tuple-serialize-to-and-deserialize-from-json). – Ron Beyer Oct 30 '15 at 13:38
  • I've made an edit above about the usage of JSon.Net. – KDecker Oct 30 '15 at 14:01
  • check my answer I fixed this with a simple [JsonIgnore] – BRAHIM Kamel Oct 30 '15 at 14:10
  • @KDecker Regarding your edit, set `ObjectCreationHandling` to `Replace`. See [Json.Net calls property getter during deserialization of list, resulting in duplicate items](http://stackoverflow.com/q/25553459/10263). – Brian Rogers Oct 31 '15 at 18:09

3 Answers3

4

The reason why you got this error because JavascriptSerializer requires a public parameter-less constructor which is not the case for Tuple because it's an immutable

take a look here http://json.codeplex.com/ for full comparison between javascriptserializer and json.net

Even microsoft states in the documentation

Json.NET should be used serialization and deserialization. Provides serialization and deserialization functionality for AJAX-enabled applications.

here an example on how you can easily use JsonConvert to achieve this

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Newtonsoft.Json;

namespace ConsoleApplication25
{
    class Program
    {
        static void Main(string[] args)
        {
            Configuration configuration = new Configuration();
            string configJson = JsonConvert.SerializeObject(configuration);
            File.WriteAllText(configPath, configJson);
            //Deserialize the  content after you read your file in string 

             var   configurationDeserialized = JsonConvert.DeserializeObject<Configuration>(configJson); 



        }
    }
}

public class Configuration
{
     [JsonIgnore]
    public List<Tuple<int, int, int>> MyThreeTuple { get; set; }

    public Configuration()
    {
        MyThreeTuple = new List<Tuple<int, int, int>>();
        MyThreeTuple.Add(new Tuple<int, int, int>(-100, 20, 501));
        MyThreeTuple.Add(new Tuple<int, int, int>(100, 20, 864));
        MyThreeTuple.Add(new Tuple<int, int, int>(500, 20, 1286));
    }
}
BRAHIM Kamel
  • 13,492
  • 1
  • 36
  • 47
  • Your example's testing is not meaningful. Since `new Configuration()` already adds the three tuples, your test can't distinguish between `MyThreeTuple` not being serialised at all, `MyThreeTuple` being serialised but ignored during deserialisation, and `MyThreeTuple` being serialised and deserialised correctly. (But even if your testing isn't right, the result could still well be.) –  Oct 30 '15 at 13:52
  • @hvd you have right but fixed with a simple [JsonIgnore] – BRAHIM Kamel Oct 30 '15 at 14:09
2

You can resolve using Newtonsoft.Json

  1. Install Newtonsoft.Json using nuget (https://www.nuget.org/packages/Newtonsoft.Json/)

Serialize :

var myObject = new Configuration();
var objString = JsonConvert.SerializeObject(myObject);

Deserialize:

 var result = JsonConvert.DeserializeObject(objString, typeof(Configuration)); 

Or

var result = JsonConvert.DeserializeObject<Configuration>(objString);
Orn Kristjansson
  • 3,435
  • 4
  • 26
  • 40
natnael88
  • 1,128
  • 10
  • 22
1

Just for completeness, I wanted to point out that it is possible to deserialize a Tuple<int, int, int> using the JavaScriptSerializer, but you need a JavaScriptConverter to do it. The converter handles instantiating the Tuple from the values in the JSON, thus avoiding the exception.

class ThreeTupleConverter : JavaScriptConverter
{
    public override IEnumerable<Type> SupportedTypes
    {
        get { return new List<Type> { typeof(Tuple<int, int, int>) }; }
    }

    public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
    {
        int item1 = (int)dictionary["Item1"];
        int item2 = (int)dictionary["Item2"];
        int item3 = (int)dictionary["Item3"];
        return new Tuple<int, int, int>(item1, item2, item3);
    }

    public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

To use the converter, you must register it with the serializer like this:

var serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new List<JavaScriptConverter> { new ThreeTupleConverter() });

Then deserialize as you normally would, and it should work fine:

string configJson = @"
{
    ""MyThreeTuple"":
    [
        { ""Item1"": -100, ""Item2"": 20, ""Item3"": 501 },
        { ""Item1"": 100, ""Item2"": 20, ""Item3"": 864 },
        { ""Item1"": 500, ""Item2"": 20, ""Item3"": 1286 }
    ]
}";

var config = serializer.Deserialize<Configuration>(configJson);

foreach (var tuple in config.MyThreeTuple)
{
    Console.WriteLine("(" + tuple.Item1 + ", " + tuple.Item2 + ", " + tuple.Item3 + ")");
}

Output:

(-100, 20, 501)
(100, 20, 864)
(500, 20, 1286)
Brian Rogers
  • 125,747
  • 31
  • 299
  • 300