Deserialization fails when converter property is missing - c#

Im using restsharp 105.2.3 and I have a model class's property (CalcDate) bound to a DateTime type but I am using a converter to parse the incoming rest response to build the timestamp. This works well, unless the svc doesn't return "calcDate"; When it's missing, the model fails to deserialized. The error that i get from IRestResponse.ErrorMessage is:
"Value to add was out of range.\r\nParameter name: value"
Interestingly if I use the raw json (with the missing calcDate) and try to construct it using jsonConvert, then it works as expected and the model is built with nulled calcDate.
> Newtonsoft.Json.JsonConvert.DeserializeObject<MyModel>(json) // works
Code:
public class MyModel {
[JsonProperty("id")]
public int Id { get; set; }
[JsonConverter(typeof(TimestampConverter))]
[JsonProperty("calcDate")]
public DateTime? CalcDate { get; set; }
}
public class TimestampConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jvalue = JValue.Load(reader);
if (jvalue.Type == JTokenType.String)
{
long val = 0;
if (long.TryParse(jvalue.ToString(), out val)) {
DateTimeOffset dto = DateTimeOffset.FromUnixTimeMilliseconds(Convert.ToInt64(val));
return dto.DateTime;
}
}
return DateTime.MinValue;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Question: How can i make restsharp use jsonConvert to deserialize the json?

Related

Deserialize JSON into a generic class with type

I have a list of JSON objects with the simplified structure shown here:
{
"id": 7,
"type": "int32",
"default": 110
}
The "type" property could vary (int16, int32, float, etc) and the "default" property should correctly reflect the type. Here is my generic C# class:
public class ConfigurationParameter<T>
{
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("default")]
public T Default { get; set; }
}
My goal here is to read in a list of these JSON objects deserialize them into the typed version of ConfigurationParameter based on the "type" property. I started by creating a custom JSON converter:
public class ConfigurationParameterConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject obj = JObject.Load(reader);
if (obj["type"] != null)
{
if(obj["type"].ToString() == "int32")
{
return obj.ToObject<ConfigurationParameter<int>>();
}
else if (obj["type"].ToString() == "float")
{
return obj.ToObject<ConfigurationParameter<float>>();
}
}
return null;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
But when I am actually deserializing the JSON string, I am not sure what type to request back since I won't know what the outputted strongly typed object will be?
//doesn't compile
var typedConfigurationParameter = JsonConvert.DeserializeObject<ConfigurationParameter<T>>(json, settings);
I am not quite sure if this is something I can't do because it can't be figured out at runtime? Or if I am possibly just not approaching the solution correctly?

Json.Net Is converting on its own before using my JsonConverter

In my WPF code, I'm using Newtonsoft.Json to deserialize json into my models. First, I receive a Json string ('json') which I then parse into 'message'. (The object I want to deserialize is wrapped in a "data" field in the json string).
Activity message = JObject.Parse(json)["data"].ToObject<Activity>();
My Activity class uses several [JsonProperty] attributes to generate its fields. One of them is an enum called 'ActivityType'.
[JsonProperty("type")]
[JsonConverter(typeof(ActivityTypeConverter))]
public ActivityType Type { get; set; }
public enum ActivityType {
EmailOpen,
LinkClick,
Salesforce,
Unsupported
};
public class ActivityTypeConverter : JsonConverter {
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var rawString = existingValue.ToString().ToLower();
if (rawString.Contains("click"))
return ActivityType.LinkClick;
else if (rawString.Contains("salesforce"))
return ActivityType.Salesforce;
else if (rawString.Contains("email_open"))
return ActivityType.EmailOpen;
else
{
Console.WriteLine("unsupported " + rawString);
return ActivityType.Unsupported;
}
}
public override bool CanConvert(Type objectType)
{
return !objectType.Equals(typeof(ActivityType));
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
What's bizarre and frustrating is that json objects which I know have "type":"email_open" are being deserialized as ActivityType.Unsupported, even though my converter should be deserializing them as EmailOpen.
Debugging has shown what the problem is:
the json field "type" is automatically deserializing "email_open" as EmailOpen and then it is sent through my converter. (It breaks then because my conditional checks for an underscore, while EmailOpen.ToString() doesn't have one.)
So my question then is: Why is it converting without my converter and how do I stop it? I just want it to only use my converter
I think your converter is being called -- it's just not working. The problem is that, rather than reading the new value from the JsonReader reader, you are using the value from the existingValue. But this second value is the pre-existing property value in the class being deserialized, not the value being read.
You need to load the value from the reader along the lines of Json.NET's StringEnumConverter. Here's a version that does that and also handles standard values of your enum by subclassing StringEnumConverter and passing the value read from the file to the base class for further processing:
public class ActivityTypeConverter : StringEnumConverter
{
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
bool isNullable = (Nullable.GetUnderlyingType(objectType) != null);
Type type = (Nullable.GetUnderlyingType(objectType) ?? objectType);
if (reader.TokenType == JsonToken.Null)
{
if (!isNullable)
throw new JsonSerializationException();
return null;
}
var token = JToken.Load(reader);
if (token.Type == JTokenType.String)
{
var rawString = ((string)token).ToLower();
if (rawString.Contains("click"))
return ActivityType.LinkClick;
else if (rawString.Contains("salesforce"))
return ActivityType.Salesforce;
else if (rawString.Contains("email_open"))
return ActivityType.EmailOpen;
}
using (var subReader = token.CreateReader())
{
while (subReader.TokenType == JsonToken.None)
subReader.Read();
try
{
return base.ReadJson(subReader, objectType, existingValue, serializer); // Use base class to convert
}
catch (Exception ex)
{
return ActivityType.Unsupported;
}
}
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(ActivityType);
}
}

Newtonsoft Json serialize a class where one of the properties is JSON [duplicate]

In order to increase performance, I have cached the result of a larger operation as JSON in a table - together with a key column to determine which row(s) to return. So the data looks some like this:
Id Json
---- ---------
1 {"property": "data", "...": "..."}
2 {"property": "data", "...": "..."}
Hence, my retrieved object has the properties int .Id and string .Json. When returning such an object with the Id, I first need to deserialize the JSON - so that it gets properly re-serialized. If I don't deserialize it first, I end up with a quoted string, i.e. my return object would look like this
{
"id": 1,
"json": "{\"property\": \"data\", ...
}
Instead, I need:
{
"id": 1,
"json": {
"property": "data",
...
}
}
Is there a way to "tell" the Json.Net serializer to output the .Json property directly without serializing - while serializing the other properties?
You could make a JsonConverter to write the raw value of the string property to the output without changing it. You take responsibility for ensuring the string has valid JSON or else the resulting output will not be valid JSON either.
Here is what the converter might look like:
class RawJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(string));
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// write value directly to output; assumes string is already JSON
writer.WriteRawValue((string)value);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// convert parsed JSON back to string
return JToken.Load(reader).ToString(Formatting.None);
}
}
To use it, mark your JSON property with a [JsonConverter] attribute like this:
class Foo
{
...
[JsonConverter(typeof(RawJsonConverter))]
public string YourJsonProperty { get; set; }
...
}
Here is a demo: https://dotnetfiddle.net/BsTLO8
Assuming you have a structure like this for serializing:
public class Record
{
[JsonProperty("id")]
public int Id
{
get;
set;
}
[JsonProperty("json")]
[JsonConverter(typeof(SpecialJsonConverter))]
public string Json
{
get;
set;
}
}
And you use code like this for serialization:
var data = new []
{
new Record() { Id=1, Json = "{\"property\":\"data\"}" },
new Record() { Id=2, Json = "{\"property\":\"data2\", \"property2\":[1, 2, 3]}" }
};
var serialized = JsonConvert.SerializeObject(data);
Console.WriteLine(serialized);
All you need is to write a proper converter for the Json property. Luckily there is a method WriteToken in the JsonWriter class that could serve our needs:
public sealed class SpecialJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var reader = new JsonTextReader(new StringReader(value.ToString()));
writer.WriteToken(reader);
}
}
Based on answer by Alex and comment by Shahin, I improved the converter a bit, and also implemented the reader to work also the other way (parse back from JToken to the string property):
public sealed class RawDataJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(string);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var tokenReader = reader as JTokenReader;
var data = tokenReader.CurrentToken.ToString(Formatting.None);
return data;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteToken(JsonToken.Raw, value);
}
}

Serialize object to JSON that already contains one JSON property

In order to increase performance, I have cached the result of a larger operation as JSON in a table - together with a key column to determine which row(s) to return. So the data looks some like this:
Id Json
---- ---------
1 {"property": "data", "...": "..."}
2 {"property": "data", "...": "..."}
Hence, my retrieved object has the properties int .Id and string .Json. When returning such an object with the Id, I first need to deserialize the JSON - so that it gets properly re-serialized. If I don't deserialize it first, I end up with a quoted string, i.e. my return object would look like this
{
"id": 1,
"json": "{\"property\": \"data\", ...
}
Instead, I need:
{
"id": 1,
"json": {
"property": "data",
...
}
}
Is there a way to "tell" the Json.Net serializer to output the .Json property directly without serializing - while serializing the other properties?
You could make a JsonConverter to write the raw value of the string property to the output without changing it. You take responsibility for ensuring the string has valid JSON or else the resulting output will not be valid JSON either.
Here is what the converter might look like:
class RawJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(string));
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// write value directly to output; assumes string is already JSON
writer.WriteRawValue((string)value);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// convert parsed JSON back to string
return JToken.Load(reader).ToString(Formatting.None);
}
}
To use it, mark your JSON property with a [JsonConverter] attribute like this:
class Foo
{
...
[JsonConverter(typeof(RawJsonConverter))]
public string YourJsonProperty { get; set; }
...
}
Here is a demo: https://dotnetfiddle.net/BsTLO8
Assuming you have a structure like this for serializing:
public class Record
{
[JsonProperty("id")]
public int Id
{
get;
set;
}
[JsonProperty("json")]
[JsonConverter(typeof(SpecialJsonConverter))]
public string Json
{
get;
set;
}
}
And you use code like this for serialization:
var data = new []
{
new Record() { Id=1, Json = "{\"property\":\"data\"}" },
new Record() { Id=2, Json = "{\"property\":\"data2\", \"property2\":[1, 2, 3]}" }
};
var serialized = JsonConvert.SerializeObject(data);
Console.WriteLine(serialized);
All you need is to write a proper converter for the Json property. Luckily there is a method WriteToken in the JsonWriter class that could serve our needs:
public sealed class SpecialJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var reader = new JsonTextReader(new StringReader(value.ToString()));
writer.WriteToken(reader);
}
}
Based on answer by Alex and comment by Shahin, I improved the converter a bit, and also implemented the reader to work also the other way (parse back from JToken to the string property):
public sealed class RawDataJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(string);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var tokenReader = reader as JTokenReader;
var data = tokenReader.CurrentToken.ToString(Formatting.None);
return data;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteToken(JsonToken.Raw, value);
}
}

Using JSON.net control serialization of final output

using the code...
[Test]
public void test()
{
var entity = new Foo();
var json = Newtonsoft.Json.JsonConvert.SerializeObject(entity);
}
against the following trivial class structure...
public class Foo
{
public Foo() { FooDate = new DateTimeWrapper(); }
public DateTimeWrapper FooDate { get; set; }
}
public class DateTimeWrapper
{
public DateTimeWrapper() { DateTime = DateTime.Now; }
public DateTime DateTime { get; set; }
}
...sets the json variable to...
{"FooDate":{"DateTime":"2013-08-30T13:36:15.4862093-05:00"}}
The JSON I want to return is...
{"FooDate":"2013-08-30T13:36:15.4862093-05:00"}
without the embedded DateTime part. How can JSON.net be used to serialize to this custom JSON and subsequently deserialize the above string to the original object?
EDIT
I am aware the object structure can be simplified to produce the desired output. However, I want to produce the output with the given object structure. This code is boiled down to highlight the problem. I didn't put all the code nor a lengthy explanation of it b/c it isn't pertinent to the question.
As I said in comments, It can easily be done by creating a custom JsonConverter
var entity = new Foo();
var json = JsonConvert.SerializeObject(entity, new DateTimeWrapperConverter());
public class DateTimeWrapperConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(DateTimeWrapper);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
//Left as an exercise to the reader :)
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var obj = value as DateTimeWrapper;
serializer.Serialize(writer, obj.DateTime);
}
}

Categories