24

Context: I need to pass an object containing a large number of properties/fields (to UI Layer from Middle Tier Layer). Among this list of properties, one is of type Version which is not getting deserialized correctly from JSON string format. I have a chosen JSON format over XML as JSON serialization to string will return short string result.

Problem: System.Version does not get deserialized correctly. I have tried two different .NET Libraries. Following are the code snippets for each:

Code Snippet 1 using ServiceStack .NET library:

        var version = new Version(1, 2, 3, 0);
        string reportJSON = JsonSerializer.SerializeToString<Version>(version);
        //{"Major":1,"Minor":2,"Build":3,"Revision":0,"MajorRevision":0,"MinorRevision":0}


        Version report2 = JsonSerializer.DeserializeFromString<Version>(reportJSON);
        string reportJSON2 = JsonSerializer.SerializeToString<Version>(report2);
        //{"Major":0,"Minor":0,"Build":-1,"Revision":-1,"MajorRevision":-1,"MinorRevision":-1}

Code Snippet 2 using Newtonsoft .NET library but with same result:

        var version = new Version(1, 2, 3, 0);
        string reportJSON = JsonConvert.SerializeObject(version);
        //{"Major":1,"Minor":2,"Build":3,"Revision":0,"MajorRevision":0,"MinorRevision":0}


        Version report2 = JsonConvert.DeserializeObject<Version>(reportJSON);
        string reportJSON2 = JsonConvert.SerializeObject(report2);
        //{"Major":0,"Minor":0,"Build":-1,"Revision":-1,"MajorRevision":-1,"MinorRevision":-1}

How to fix this? Or Which other JSON.NET library can work correctly?

monish001
  • 671
  • 2
  • 8
  • 20
  • possible duplicate of [Can I deserialize to an immutable object using JavascriptSerializer?](http://stackoverflow.com/questions/4080644/can-i-deserialize-to-an-immutable-object-using-javascriptserializer) – spender Nov 01 '12 at 03:20
  • @spender: but it works fine with string objects which are immutable. – monish001 Nov 01 '12 at 05:03
  • 1
    @Monish [Here](http://stackoverflow.com/a/2085890/1180426) is a link that explains why the `Version` class is not XML-serializable, but I'm pretty sure same mechanism applies to JSON serializers. – Patryk Ćwiek Nov 01 '12 at 07:57

2 Answers2

29

The Newtonsoft.Json library provides a set of common converters in the Newtonsoft.Json.Convertersnamespace, including a VersionConverter you can use to serialize and deserialize System.Version.

Note that you have to use the VersionConverterboth for serialization and deserialization, though.
That's because standard serialization would generate eg.:{"Major":1,"Minor":2,"Build":3,"Revision":0,"MajorRevision":0,"MinorRevision":0} while VersionConverterdeserialization expects a simple string as in "1.2.3".

So usage would be:

using Newtonsoft.Json;
using Newtonsoft.Json.Converters;  

string s = JsonConvert.SerializeObject(version, new VersionConverter());
Version v = JsonConvert.DeserializeObject<Version>(s, new VersionConverter());

I'm not sure what's the first version of Newtonsoft.Jsonthat includes that converter. Mine has it and it's 5.0.6.

Manuel
  • 391
  • 3
  • 3
  • Worked for me, only I had to make a little change since I'm using the DeserializeObject signature that gets the JsonSerializerSettings object: settings.Converters.Add(new VersionConverter()); (no overload takes both settings and a collection of converters) – Alonzzo2 May 15 '17 at 16:41
13

The properties of the Version class have no setter. They just return the value of their corresponding private fields. Therefore, the deserializer is not able to change their values.

But with Json.NET you can write a custom converter class which handles the deserialization of the Version class.

Beware: This code has not been tested very well...

public class VersionConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        // default serialization
        serializer.Serialize(writer, value);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        // create a new Version instance and pass the properties to the constructor
        // (you may also use dynamics if you like)
        var dict = serializer.Deserialize<Dictionary<string, int>>(reader);
        return new Version(dict["Major"], dict["Minor"], dict["Build"], dict["Revision"]);
    }

    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(Version);
    }
}

Then you have to specify that you want to use the converter:

var v = new Version(1, 2, 3, 4);
string json = JsonConvert.SerializeObject(v);

var v2 = JsonConvert.DeserializeObject<Version>(json, new VersionConverter());

Json.NET decides itself whether to use one of the converters you specified. So you can always specify the converter, as shown below. Json.NET will use one of your converters if they match a type within SomeClass.

var result = JsonConvert.DeserializeObject<SomeClass>(json, new VersionConverter());
fero
  • 6,050
  • 1
  • 33
  • 56
  • What if I have a class called Userinfo and inside the class there is a variable like Dictionary? This var result = JsonConvert.DeserializeObject(json, new VersionConverter()); doesn't work – Seaky Lone May 26 '18 at 00:17
  • With Json.NET 11 (maybe also earlier versions), you can just serialize/deserialize a `Version` object with the default serializer as it doesn't result in an object with `Major` and `Minor` properties etc. anymore. The default serialization result of a `Version` is now a string like `"1.2.3.4"`. And deserialization works fine. Hence using it as a key in a dictionary is not a problem. (I just tested it and it works perfectly.) – fero May 28 '18 at 06:42
  • Please note that this will not work for objects with only some parts set e.g. 2.0. The oter parts are serialised as `-1` which then causes an exception at `new Version(...)` – tymtam Jan 06 '21 at 07:54
  • So they changed the way it's serialized, and also what it expects to be able to deserialize, with no respect for backward compatibility. Thus breaking anything in existence that was already serializing a Version object the old way. Wonderful. – user1169420 Apr 29 '21 at 16:20
  • 1
    To clarify: As of may 2021, NET Framework builds with newtonsoft 13 can deserialize from either format. Dotnet core builds with newtonsoft 13 can only deserialize from string format. – user1169420 Apr 30 '21 at 15:31