EJoshuaS EJoshuaS - 3 months ago 12
JSON Question

System.ArgumentOutOfRangeException while deserializing using Newtonsoft.Json

I am using Newtonsoft JSON to deserialize an object that contains interfaces. Newtonsoft was having trouble "figuring out" how to map the interfaces to their concrete types on deserialization, so I was following the directions in this answer to fix the issue.

I am doing the following to deserialize:

var converter = new JsonSerializer();
converter.Converters.Add(new DeviceCalibrationConverter());

// Obviously parameter.value and typeObj being the JSON and Type respectively
// I can see stepping through this that these are, in fact, the correct values
object deserialized = converter.Deserialize(new StringReader(parameter.Value), typeObj);


I'm using the DeviceCalibrationConverter object to try to map my interface to its concrete type:

public class DeviceCalibrationConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
// I am trying to map the IDeviceCalibration interface to its concrete type (DeviceCalibration)
return objectType.Equals(typeof(IDeviceCalibration));
}

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return serializer.Deserialize(reader, typeof(DeviceCalibration));
}

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
}


I'm currently getting an "ArgumentOutOfRangeException." Full exception details are below:

System.ArgumentOutOfRangeException was unhandled
HResult=-2146233086
Message=Version's parameters must be greater than or equal to zero.
Parameter name: build
Source=mscorlib
ParamName=build
StackTrace:
at System.Version..ctor(Int32 major, Int32 minor, Int32 build, Int32 revision)
at Void .ctor(Int32, Int32, Int32, Int32)(Object[] )
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObjectUsingCreatorWithParameters(JsonReader reader, JsonObjectContract contract, JsonProperty containerProperty, ObjectConstructor`1 creator, String id)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateNewObject(JsonReader reader, JsonObjectContract objectContract, JsonProperty containerMember, JsonProperty containerProperty, String id, Boolean& createdFromNonDefaultCreator)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.SetPropertyValue(JsonProperty property, JsonConverter propertyConverter, JsonContainerContract containerContract, JsonProperty containerProperty, JsonReader reader, Object target)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.SetPropertyValue(JsonProperty property, JsonConverter propertyConverter, JsonContainerContract containerContract, JsonProperty containerProperty, JsonReader reader, Object target)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
at Newtonsoft.Json.JsonSerializer.Deserialize(TextReader reader, Type objectType)
at FunctionalTesting.ExecuteXMLScript.Execute() in [folder]\ExecuteXMLScript.cs:line 141
at FunctionalTesting.TestRunner.RunTests() in [folder]\TestRunner.cs:line 102
at FunctionalTesting.Program.Main(String[] args) in [folder]\Program.cs:line 43
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
InnerException:


This occurs, by the way, on the line where I try to call Deserialize.

Edit: The entire JSON is pretty lengthy but it turns out that the offending line is as follows:

{"State":"needs-translation","OriginalString":"LP","StringID":[id],"StringValue":"LP"}}],
"MarketingFeatures":null,
"CDIDriver" {"Name":"[Product Name]",
"Version":{"Major":1,"Minor":0,"Build":-1,"Revision":-1,"MajorRevision":-1,"MinorRevision":-1}}


In particular, in "Version" deserializes to:

{"Major":1,"Minor":0,"Build":-1,"Revision":-1,"MajorRevision":-1,"MinorRevision":-1}


This deserializes to the System.Version class and this is invalid, thus producing the exception I listed above.

CDIDriver, by the way, creates the Version object as follows:

Version = new Version((int)major, (int)minor);


This is perfectly valid, and the document does, in fact, say that using this constructor as described will set the build and revision to -1 (as shown in the JSON). My question, then, is if this is a perfectly valid, document state of the object, why does it produce this exception when I try to deserialize it?

I am aware that trying to do something like
new Version(1, 0, -1, -1)
will produce an exception and that this is the documented behavior. (This seems like very odd behavior given that that would result in a valid object state, but that's just my opinion). Is there some way around having to do:

new Version(1, 0, 0, 0)


just for the sake of making the deserialization work?

Answer

The Version has not been serialized in the right way.

If done correctly it should loke like this:

{
    ...
    "Version": "1.0"
}

This can be achieved by using the VersionConverter like so:

var json = JsonConvert.SerializeObject(new Version(1, 0), new VersionConverter());

Deserialization also has to use this converter:

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

Working example: https://dotnetfiddle.net/eAqwip

Note that you can also annotate the de/serialized class with a JsonConverterAttribute to achieve the same automatically:

public class DeviceCalibration
{
    ...

    [JsonConverter(typeof(VersionConverter))]
    public Version Version { get; set }
}

If you don't have access to the serializer code, I'm afraid you will have to fix the Json string by hand or write your own VersionConverter that can handle -1 values.