JsonConverter
CanConvert
always returns true.ReadJson
I want to sometimes just use the "default" behavior, as if CanConvert
had returned false and my ReadJson
was never called.
existingValue = existingValue ?? serializer
.ContractResolver
.ResolveContract(objectType)
.DefaultCreator();
serializer.Populate(reader, existingValue);
NullReferenceException
on .DefaultCreator()
. existingValue
is always null
ContractResolver
that is returned from the serializer is my own. It extends json.net's built-in CamelCasePropertyNamesContractResolver
and simply overrides the methods CreateConstructorParameters
and CreatePropertyFromConstructorParameter
How do I tell json.net - "just kidding, I don't know how to create this thing, do whatever you would have done to create it had I told you that I couldn't create it"
Note that I've simplified the problem for discussion. I am anticipating someone will answer with "just have CanCreate
return false
" In fact, in a number of scenarios I can and should create the object.
From your question and comments, it sounds like you have some situations where you want a converter to read but not write, and others where you want it to write but not read. You've solved the problem by splitting the functionality into two converters and then having each converter's CanConvert
method return true or false at the appropriate times. This is certainly a viable approach and seems to be working for you, which is great. However, I wanted to offer an alternative solution.
In addition to the CanConvert
method, the base JsonConverter
offers two virtual boolean properties which you can override: CanRead
and CanWrite
. (Both return true by default.) These properties directly control whether ReadJson
and WriteJson
are called by the serializer for a particular converter. So, for example, if CanRead
returns false, then ReadJson
will not be called and the default read behavior will be used instead, even though CanConvert
returned true. This allows you to set up an asymmetric converter quite neatly. For example, you might have a situation where you want to deserialize a crazy JSON format into a more sane object structure, but when you serialize it again, you don't want to go back to the crazy JSON format-- you just want the default serialization. In that case you would override CanWrite
in your converter to always return false. Then you could just leave the implementation of WriteJson
blank or have it throw a NotImplementedException
; it will never be called.
Your case sounds a little more complicated than that, but you still should be able to manipulate the CanRead
and CanWrite
properties to achieve your desired results. Below is a contrived example which shows how we can switch the ReadJson
and WriteJson
methods on and off depending on a situational variable.
public class Program
{
public static void Main(string[] args)
{
string json = @"{""keys"":[""foo"",""fizz""],""values"":[""bar"",""bang""]}";
CustomConverter converter = new CustomConverter();
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.Converters.Add(converter);
// Here we are reading a JSON object containing two arrays into a dictionary
// (custom read) and then writing out the dictionary JSON (standard write)
Console.WriteLine("--- Situation 1 (custom read, standard write) ---");
converter.Behavior = ConverterBehavior.CustomReadStandardWrite;
json = DeserializeThenSerialize(json, settings);
// Here we are reading a simple JSON object into a dictionary (standard read)
// and then writing out a new JSON object containing arrays (custom write)
Console.WriteLine("--- Situation 2 (standard read, custom write) ---");
converter.Behavior = ConverterBehavior.StandardReadCustomWrite;
json = DeserializeThenSerialize(json, settings);
}
private static string DeserializeThenSerialize(string json, JsonSerializerSettings settings)
{
Console.WriteLine("Deserializing...");
Console.WriteLine(json);
var dict = JsonConvert.DeserializeObject<Dictionary<string, string>>(json, settings);
foreach (var kvp in dict)
{
Console.WriteLine(kvp.Key + ": " + kvp.Value);
}
Console.WriteLine("Serializing...");
json = JsonConvert.SerializeObject(dict, settings);
Console.WriteLine(json);
Console.WriteLine();
return json;
}
}
enum ConverterBehavior { CustomReadStandardWrite, StandardReadCustomWrite }
class CustomConverter : JsonConverter
{
public ConverterBehavior Behavior { get; set; }
public override bool CanConvert(Type objectType)
{
return typeof(IDictionary<string, string>).IsAssignableFrom(objectType);
}
public override bool CanRead
{
get { return Behavior == ConverterBehavior.CustomReadStandardWrite; }
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
Console.WriteLine("ReadJson was called");
// Converts a JSON object containing a keys array and a values array
// into a Dictionary<string, string>
JObject jo = JObject.Load(reader);
return jo["keys"].Zip(jo["values"], (k, v) => new JProperty((string)k, v))
.ToDictionary(jp => jp.Name, jp => (string)jp.Value);
}
public override bool CanWrite
{
get { return Behavior == ConverterBehavior.StandardReadCustomWrite; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
Console.WriteLine("WriteJson was called");
// Converts a dictionary to a JSON object containing
// a keys array and a values array from the dictionary
var dict = (Dictionary<string, string>)value;
JObject jo = new JObject(new JProperty("keys", new JArray(dict.Keys)),
new JProperty("values", new JArray(dict.Values)));
jo.WriteTo(writer);
}
}
Output:
--- Situation 1 (custom read, standard write) ---
Deserializing...
{"keys":["foo","fizz"],"values":["bar","bang"]}
ReadJson was called
foo: bar
fizz: bang
Serializing...
{"foo":"bar","fizz":"bang"}
--- Situation 2 (standard read, custom write) ---
Deserializing...
{"foo":"bar","fizz":"bang"}
foo: bar
fizz: bang
Serializing...
WriteJson was called
{"keys":["foo","fizz"],"values":["bar","bang"]}
Fiddle: https://dotnetfiddle.net/BdtSoN
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With