I have a JsonConverter
that, depending on an instance specific flag, needs to either
- run custom serialization logic
- run the default Json.NET serialization logic
How can the default Json.NET serialization logic be ran from a JsonConverter
?
Thanks
I have a JsonConverter
that, depending on an instance specific flag, needs to either
How can the default Json.NET serialization logic be ran from a JsonConverter
?
Thanks
Here is an example. Say your class to serialize looks like this:
class Foo
{
public bool IsSpecial { get; set; }
public string A { get; set; }
public string B { get; set; }
public string C { get; set; }
}
The IsSpecial
flag is used to control whether we do something special in the converter or just let things serialize naturally. You can write your converter like this:
class FooConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(Foo).IsAssignableFrom(objectType);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
Foo foo = (Foo)value;
JObject jo;
if (foo.IsSpecial)
{
// special serialization logic based on instance-specific flag
jo = new JObject();
jo.Add("names", string.Join(", ", new string[] { foo.A, foo.B, foo.C }));
}
else
{
// normal serialization
jo = JObject.FromObject(foo);
}
jo.WriteTo(writer);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Then, to use the converter, pass an instance of it to the SerializeObject
method (e.g. in the settings). (Do NOT decorate the target class with a JsonConverter
attribute, or this will result in an infinite recursive loop when you serialize.)
class Program
{
static void Main(string[] args)
{
List<Foo> foos = new List<Foo>
{
new Foo
{
A = "Moe",
B = "Larry",
C = "Curly",
IsSpecial = false
},
new Foo
{
A = "Huey",
B = "Dewey",
C = "Louie",
IsSpecial = true
},
};
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.Converters.Add(new FooConverter());
settings.Formatting = Formatting.Indented;
string json = JsonConvert.SerializeObject(foos, settings);
Console.WriteLine(json);
}
}
Output:
[
{
"IsSpecial": false,
"A": "Moe",
"B": "Larry",
"C": "Curly"
},
{
"names": "Huey, Dewey, Louie"
}
]
FooConverter
also needs to be included in the serialization process. This then causes the "normal" serialization to go through the FooConverter
again, with IsSpecial = false
. Stated differently, the problem was to serialize a field encountered during serialization differently than an object passed directly into JsonConvert.Serialize
. A (non-optimal) solution is to require that all field references be annotated with a [JsonConverter]
reference. –
Durer JsonSerializer
into JObject.FromObject
. For example: var jo = JObject.FromObject(value, JsonSerializer.CreateDefault(new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }));
–
Ocieock You can change the CanWrite property to disable a custom serializer. This won't work right if the object can contain children of the same type or if you are serializing in more than one thread.
class FooConverter : JsonConverter
{
bool _canWrite = true;
public override bool CanWrite
{
get { return _canWrite;}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
Foo foo = (Foo)value;
JObject jo;
if (foo.IsSpecial)
{
// special serialization logic based on instance-specific flag
jo = new JObject();
jo.Add("names", string.Join(", ", new string[] { foo.A, foo.B, foo.C }));
}
else
{
// normal serialization
_canWrite = false;
jo = JObject.FromObject(foo);
_canWrite = true;
}
jo.WriteTo(writer);
}
}
CanRead
and CanWrite
to prevent recursion is working perfectly for me. -- Though, I'm using serializer.Serialize(writer, value);
to write the value, and I recommend setting the value back to the original value in a finally
block so that it never gets stuck in a weird state. –
Sorghum I have used the following Newtonsoft.Json converter to partially customize deserialization. My implementation demonstrates the following:
This example can be adapted to conditionally customize serialization behavior. Another answer on StackOverflow used a [ThreadStatic] static bool IsEnabled
along with override bool CanWrite => IsEnabled
. The WriteJson
method would temporarily disable the converter, recurse using serializer.Serialize(writer, value)
, and in a finally
block re-enable the converter. That way, the converter can choose to skip affecting the serialization behavior.
public sealed class CustomJsonConverter<
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] T>
: JsonConverter<T>
{
public override bool CanWrite => false;
public override bool CanRead => true;
public override void WriteJson(JsonWriter writer, T? value, JsonSerializer serializer)
{
throw new NotSupportedException("This type is only used for deserialization.");
}
public override T? ReadJson(JsonReader reader, Type objectType, T? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return default;
var result = (T)RuntimeHelpers.GetUninitializedObject(typeof(T));
serializer.Populate(reader, result);
return result;
}
}
© 2022 - 2024 — McMap. All rights reserved.