Why JsonConverter.WriteJson() never gets called, although JsonConverter.ReadJson() does get called? - c#

Why my custom JsonConverter.WriteJson() method doesn't get called ?
class MyType{
[JsonConverter(typeof(DocumentXamlDeserializer))]
public string GuiData { get; set; }
public string SimpleString;
}
Although the ReadJson does get called:
public class DocumentXamlDeserializer : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(Gui.Handler.SerializeData());
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var xaml = reader.Value as string;
Gui.Handler.DeserializeData(xaml);
return xaml;
}
public override bool CanConvert(Type objectType) { return typeof(string).IsAssignableFrom(objectType); }
}
The serialization call: JsonConvert.SerializeObject(dataModel, Formatting.Indented);
The deserialization call: JsonConvert.DeserializeObject<Model>(raw);

Apparently this is because GuiData is null... I guess I could specify:
TypeNameHandling = TypeNameHandling.Objects
But I want to serialize only GuiData even if it's null (I set its value during serialization), without serializing all null-properties... well, if I don't find better way, I guess I'd have to suffice with it...

Related

GraphQLHttpClient nested json string c# [duplicate]

Same question as Deserializing stringified (quote enclosed) nested objects with Jackson but for C# and Newtonsoft Json.Net. The question is how to deserialize a nested string representation of JSON using Json.Net. Example response from REST endpoint:
{
"Id": "abcd1234",
"Foo": "{\"field1\": "abc", \"field2\": \"def\"}"
}
C# Model:
public class Model {
public Guid Id { get; set; }
public Dictionary<string, string> Foo { get; set; }
}
Just calling JsonConvert.DeserializeObject<Model>(returnedJson) will error out because Foo in its returned form is stringified JSON, aka just a string. This is the case for other fields in our JSON response as well that have different Dictionary types associated in their models (e.g. Dictionary<string, int> and Dictionary<string, DateTime>).
What's the simplest way to handle this? Is a custom JsonConverter necessary or is there some built-in way to handle it?
A JsonConverter is probably the simplest way to handle this implicit conversion. Here's one that works for your case.
public class MyConverter: JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(string);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return JsonConvert.DeserializeObject(reader.Value as string, objectType);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
And then you decorate that property with the attribute like this:
public class Model
{
public string Id { get; set; }
[JsonConverter(typeof(MyConverter))]
public Dictionary<string, string> Foo { get; set; }
}
Just to follow up, I used a very similar solution to what #VijayeR recommended, but needed to tweak it a bit as it was still causing some problems for me. One issue is that when using the decorator, apparently CanConvert doesn't get called at all because Json.Net assumes you've assigned the correct converter to that property. This is what worked for me:
public class NestedJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jToken = JToken.Load(reader);
if (jToken.Type == JTokenType.String)
{
jToken = JToken.Parse((string) jToken);
}
return jToken.ToObject(objectType);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanWrite => false;
}

Deserializing stringified (quote enclosed) nested objects with Newtonsoft Json.Net

Same question as Deserializing stringified (quote enclosed) nested objects with Jackson but for C# and Newtonsoft Json.Net. The question is how to deserialize a nested string representation of JSON using Json.Net. Example response from REST endpoint:
{
"Id": "abcd1234",
"Foo": "{\"field1\": "abc", \"field2\": \"def\"}"
}
C# Model:
public class Model {
public Guid Id { get; set; }
public Dictionary<string, string> Foo { get; set; }
}
Just calling JsonConvert.DeserializeObject<Model>(returnedJson) will error out because Foo in its returned form is stringified JSON, aka just a string. This is the case for other fields in our JSON response as well that have different Dictionary types associated in their models (e.g. Dictionary<string, int> and Dictionary<string, DateTime>).
What's the simplest way to handle this? Is a custom JsonConverter necessary or is there some built-in way to handle it?
A JsonConverter is probably the simplest way to handle this implicit conversion. Here's one that works for your case.
public class MyConverter: JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(string);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return JsonConvert.DeserializeObject(reader.Value as string, objectType);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
And then you decorate that property with the attribute like this:
public class Model
{
public string Id { get; set; }
[JsonConverter(typeof(MyConverter))]
public Dictionary<string, string> Foo { get; set; }
}
Just to follow up, I used a very similar solution to what #VijayeR recommended, but needed to tweak it a bit as it was still causing some problems for me. One issue is that when using the decorator, apparently CanConvert doesn't get called at all because Json.Net assumes you've assigned the correct converter to that property. This is what worked for me:
public class NestedJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jToken = JToken.Load(reader);
if (jToken.Type == JTokenType.String)
{
jToken = JToken.Parse((string) jToken);
}
return jToken.ToObject(objectType);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanWrite => false;
}

Creating a custom JsonConverter to handle System.Text.Encoding objects

I have written a custom JsonConverter which I am hoping will allow me to serialize and deserialize Encoding objects within my classes:
public class EncodingConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType.IsSubclassOf(typeof(Encoding));
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(((Encoding)value).EncodingName);
}
public override bool CanRead { get { return true; } }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var name = reader.ReadAsString();
return Encoding.GetEncoding(name);
}
}
However, when I run the following test code, I get an exception when calling DeserializeObject, and the ReadJson method never gets called.
class Program
{
private static void Main(string[] args)
{
var test = new TestClass();
var jsonSettings = new JsonSerializerSettings
{
Converters = new[] { new EncodingConverter(), }
};
var json = JsonConvert.SerializeObject(test, jsonSettings);
var test2 = JsonConvert.DeserializeObject<TestClass>(json, jsonSettings);
}
}
class TestClass
{
public string Property1;
public Encoding Encoding = Encoding.UTF8;
}
The exception message is:
Target type System.Text.Encoding is not a value type or a non-abstract class.
Am I missing something?
There are three problems with your converter that I see.
You are using the wrong check in CanConvert().
You are using the wrong name for the Encoding when serializing.
You are using the wrong method to get the value from the reader when deserializing.
Let's take these one at a time.
First, in your CanConvert method you are using objectType.IsSubclassOf(typeof(Encoding)) to determine whether the converter should handle the Encoding. This works fine on serialization because you have a concrete instance of the encoding (e.g. UTF8Encoding), which is indeed a subclass of Encoding. However, on deserialization, the deserializer doesn't know what concrete type of encoding you are going to make, so the type that is passed to the converter is just Encoding. Since Encoding is not a subclass of itself, CanConvert returns false, and your ReadJson method never gets called. That leaves Json.Net to try to instantiate the Encoding itself, which it can't do (because Encoding is abstract), so it throws the error you mentioned in your question. You should instead use typeof(Encoding).IsAssignableFrom(objectType) inside your CanConvert method.
Second, when serializing the Encoding inside WriteJson, you are outputting the EncodingName property, which is the human-readable display name of the encoding, not the code page name. If you look at the documentation for the Encoding.GetEncoding(string) method, it says:
Parameters
name
Type: System.String
The code page name of the preferred encoding. Any value returned by the WebName property is valid. Possible values are listed in the Name column of the table that appears in the Encoding class topic.
So, you should be outputting the value of the WebName property in your WriteJson method if you want to be able to use this value to later reconstruct the Encoding in ReadJson.
Third, in your ReadJson method you are using reader.ReadAsString() to attempt to get the encoding name from the JSON. This will not work as you expect. When ReadJson is called by Json.Net, the reader is already positioned at the current value. When you call ReadAsString(), that advances the reader to the next token and then attempts to interpret that token as a string. What you really want to do is just get the value of the current token, which you can do using the Value property. Because Value is of type object, you will need to cast it to a string.
Here is the corrected code for the converter:
public class EncodingConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(Encoding).IsAssignableFrom(objectType);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(((Encoding)value).WebName);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return Encoding.GetEncoding((string)reader.Value);
}
}
Fiddle: https://dotnetfiddle.net/UmLynX
Try:
public class CustomConverter : JsonConverter
{
public override bool CanConvert(System.Type objectType)
{
return true;// objectType.IsAssignableFrom(typeof(Encoding));
}
public override object ReadJson(JsonReader reader, System.Type objectType, object existingValue, JsonSerializer serializer)
{
return Encoding.GetEncoding(Convert.ToString(reader.Value));
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var t = (Test)value;
var e = (Encoding)t.MyProperty;
writer.WriteValue(e.BodyName);
//serializer.Serialize(writer, e.BodyName);
}
}
And in Main:
var o = new Test { MyProperty = Encoding.UTF8 };
var s = new JsonSerializerSettings
{
Converters = new[] { new CustomConverter() }
};
var v = JsonConvert.SerializeObject(o, s);
var o2 = new Test();
o2.MyProperty = Encoding.GetEncoding(JsonConvert.DeserializeObject(v, s).ToString());

Newtonsoft.Json library and serializing .NET decimal type [duplicate]

I'm serializing decimals using Newtonsoft.Json.JsonSerializer.
How can I set it to serialize decimal numbers with only 1 decimal place to use 0 at the end.
i.e. 3.5 serializes to "3.50"?
You'll have to write your own custom JsonConverter and use it to intercept the decimal type so you can change how it gets serialized. Here's an example:
public class DecimalFormatConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(decimal));
}
public override void WriteJson(JsonWriter writer, object value,
JsonSerializer serializer)
{
writer.WriteValue(string.Format("{0:N2}", value));
}
public override bool CanRead
{
get { return false; }
}
public override object ReadJson(JsonReader reader, Type objectType,
object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
To use it, simply pass in a new instance of this custom converter to the SerializeObject method:
var json = JsonConvert.SerializeObject(yourObject, new DecimalFormatConverter());
The accepted answer is correct, but expanding upon the comments on accepted answer:
If you want the decimals in your JSON to be numbers instead of strings, you need to use WriteRawValue and use :0.00 instead of :N2 for the string formatting (as N2 includes thousand separator commas and other culture specific number formatting that will break your JSON)
public class DecimalFormatConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(decimal);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteRawValue($"{value:0.00}");
}
public override bool CanRead => false;
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
This is the difference in output compared to the accepted answer.
# writer.WriteRawValue($"{value:0.00}");
{
"MyDecimal": 3.50,
"MyBiggerDecimal": 12345.50
}
# writer.WriteValue($"{value:N2}");
{
"MyDecimal": "3.50",
"MyBiggerDecimal": "12,345.50"
}
Note - the accepted answer is correct for the OP's specific question i.e. serialize 3.5 to "3.50", but I got here wanting to serialize 3.5 to 3.50 (without the string quotes).
Hy!
The second answer is also correct, but not reflecting Cultures.
If you want to have really 0.00 (with .) you have to use:
public class DecimalFormatConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(decimal);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
FormattableString formattableString = $"{value:0.00}";
writer.WriteRawValue(formattableString.ToString(CultureInfo.InvariantCulture));
}
public override bool CanRead => false;
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
With this it is garantueed to have dot as decimal separater.
The difference is:
FormattableString formattableString = $"{value:0.00}";
writer.WriteRawValue(formattableString.ToString(CultureInfo.InvariantCulture));

How can I use JSON.NET to handle a value that is sometimes an object and sometimes an array of the object?

I notice there are some other results on stackoverflow for this question but they don't seem to work or are vague. Using the most popular result, I have put together this:
The problem is that when JSON comes back and is being serialised into one of my custom types, one of the bits of JSON is sometimes an array, and sometimes just a string. If my custom type has a string, and the JSON is an array, I get an error. The same happens the other way around, if the JSON is an object and my custom type is an array. It just can't map it to the property.
I decided to solve this, I want to override the deserialisation of this particular property, and if it's an object, I want to convert it into an array of 1 object.
In the object I am serialising to, I added a JsonConverter which I think is going to override the way it's deserialised.
[JsonConverter(typeof(ArrayOrSingleObjectConverter<string>))]
public List<string> person { get; set; }
The idea is that the custom converter will convert a single object to an array. So if the JSON is "Hello" it will set person to be a List containing "Hello" instead of throwing an exception saying cannot convert string to List.
If it's already an array, it should just leave it alone.
The converter looks like this:
public class ArrayOrSingleObjectConverter<T> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true; // Not sure about this but technically it can accept an array or an object, so everything is game.
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (objectType == typeof(List<T>))
{
return serializer.Deserialize<List<T>>(reader);
}
else
{
var singleObject = serializer.Deserialize<T>(reader);
var objectAsList = new List<T>();
objectAsList.Add(singleObject);
return objectAsList;
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
It doesn't work. The above code throws an exception trying to deserialize a a single string saying it can't cast it into a List inside the if statement (the 'objectype' is however a List).
I'm struggling to understand exactly what the read and write methods are doing. In the other answer on stackoverflow, it suggests throwing a NotImplementedException in the read method. But if I do that, the read method is called and the exception throws.
I think I'm on the right track, but I need a nudge in the right direction. I think I'm a little confused about what the ReadJSon method is doing and what its parameters mean.
I don't really understand where the value is coming from that it's deserializing since I didn't specify it in the Deserialize method call.
I'm a bit out of my depth on this one.
I had to do something similar last week and I came up with the following, which works fine for a List rather than an array
internal class GenericListCreationJsonConverter<T> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override bool CanRead
{
get { return true; }
}
public override bool CanWrite
{
get { return false; }
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.StartArray)
{
return serializer.Deserialize<List<T>>(reader);
}
else
{
T t = serializer.Deserialize<T>(reader);
return new List<T>(new[] { t });
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
I like this method which makes Json.NET do all the heavy lifting. And as a result, it supports anything that Json.NET supports (List<>, ArrayList, strongly-typed arrays, etc).
public class FlexibleCollectionConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.StartArray)
{
return serializer.Deserialize(reader, objectType);
}
var array = new JArray(JToken.ReadFrom(reader));
return array.ToObject(objectType);
}
public override bool CanConvert(Type objectType)
{
return typeof (IEnumerable).IsAssignableFrom(objectType);
}
}

Categories

Resources