Reputation: 253
I have a class Employee where Manager property itself is of type Employee
public class Employee
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Employee Manager { get; set; }
public IList<string> Roles { get; set; }
}
I want to create a custom JsonConverter for Employee type.
public class TestJsonConverter : JsonConverter
{
public TestJsonConverter()
{
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JToken t = JToken.FromObject(value);
if (t.Type != JTokenType.Object)
{
t.WriteTo(writer);
}
else
{
JObject o = (JObject)t;
IList<string> propertyNames = o.Properties().Select(p => p.Name).ToList();
o.AddFirst(new JProperty("Keys", new JArray(propertyNames)));
o.WriteTo(writer);
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter.");
}
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(Employee));
}
public override bool CanRead
{
get { return false; }
}
}
The ContractResolver is
class ContractResolver : DefaultContractResolver
{
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
JsonObjectContract contract = base.CreateObjectContract(objectType);
if (objectType == typeof(Employee))
{
contract.Converter = new TestJsonConverter();
}
return contract;
}
}
When I try to serialize the Employee object, custom JsonConverter is getting called only for the top level Employee object, not for the nested Manager property which is also of Employee type:
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.ContractResolver = new ContractResolver();
string json = JsonConvert.SerializeObject(employee, Formatting.Indented, settings);
Upvotes: 6
Views: 7718
Reputation: 711
I think you run into the same issue described in Custom JsonConverter WriteJson Does Not Alter Serialization of Sub-properties:
The reason that your converter is not getting applied to your child objects is because JToken.FromObject() uses a new instance of the serializer internally, which does not know about your converter.
The solution provided should be adaptable to your case.
Upvotes: 6
Reputation: 2173
So the JsonConverter
runs once, starting at the root node.
You will need to navigate this JSon Object tree and update it yourself.
Not sure it this is what you intended to achieve but I've tried it myself and you can decide if this works for you or not.
public class TestJsonConverter : JsonConverter
{
public TestJsonConverter()
{
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JToken t = JToken.FromObject(value);
if (t.Type != JTokenType.Object)
{
t.WriteTo(writer);
}
else
{
JObject root = (JObject)t;
var stack = new Stack<JObject>();
stack.Push(root);
while (stack.Any())
{
var current = stack.Pop();
var propertyNames = current.Properties().Select(p => p.Name).ToArray();
current.AddFirst(new JProperty("Keys", new JArray(propertyNames)));
var nestedObjects = current.Properties().Where(p => p.Value.Type == JTokenType.Object).ToArray();
foreach (var nestedObj in nestedObjects)
{
stack.Push((JObject)nestedObj.Value);
}
}
root.WriteTo(writer);
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter.");
}
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(Employee));
}
public override bool CanRead
{
get { return false; }
}
}
Upvotes: 0