Adding custom NewtonSoft JsonConverter breaks client side viewmodel serialization

Hello DotVVM team! I’ve run into a problem where I’m trying to add a NewtonSoft CustomCreationConverter in order to serialize additional type information for an abstract class I’m defining on one of my ViewModels. The issue is that, during viewmodel validation happening client side (before making POST call), dotvvm internal throws this error.

I’ve narrowed it down to the addition of the C# converter attribute as what triggers this error.

[Serializable]
[JsonConverter(typeof(AbstractCustomRuleJsonConverter))] // the addition of this line induces client side error
public abstract partial class AbstractCustomRule : CustomRule, ICustomRule

Seeing as this error was clientside, I decided to diff the serialized viewmodel, and it looks like as a side effect of adding the converter, the $type property is missing in viewmodel json. See below image of left side with converter missing $type vs right side without converter including it.

A further confusing point is that the CustomCreationConverter is only meant to modify deserialization but the initial page load serialization of ViewModel is what is being affected.

Below is my Converter impl

using System;

using CustomRules.DocumentRules;
using CustomRules.FolderRules;
using CustomRules.ScheduledRules;

using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;

namespace CustomRules.Common;

public class AbstractCustomRuleJsonConverter : CustomCreationConverter<AbstractCustomRule>
{
	private string _ruleTypeName;

	public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
	{
		var jsonObj = JToken.ReadFrom(reader);
		_ruleTypeName = jsonObj["RuleTypeName"].ToString();
		return base.ReadJson(jsonObj.CreateReader(), objectType, existingValue, serializer);
	}

	public override AbstractCustomRule Create(Type objectType)
	{
		return _ruleTypeName switch
		{
			"DocumentRule" => new DocumentRule(),
			"FolderRule" => new FolderRule(),
			"ScheduledRule" => new ScheduledRule(),
			_ => throw new NotImplementedException(),
		};
	}
}

Hello! The crash you are getting is definitely a bug, we’re fixing that in #1803.

However, I’m not sure that actually solves the problem for you. Custom converters are generally quite tricky, since DotVVM client-side essentially assumes that the view model is serialized by the DotVVM converter. In your case, I guess the CustomCreationConverter should let the DotVVM ViewModelJsonConverter do the serialization and only call your code for deserialization. I have no idea what that doesn’t work, could you please try inheriting from the general JsonConverter and manually handing the serialization to ViewModelJsonConverter. Something like the following should do the trick

public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
{
    serializer.Converters.OfType<ViewModelJsonConverter>().First().WriteJson(writer, value, serializer);
}

Thanks for the response @exyi. I also had a similar thought of extending the DotVVM one but I ended up finding a different root cause in that, my abstract class property was also in a class ambiguous object dictionary (a migration we’re doing of WebForms’ ViewState.)

Once I promoted my property as first class citizen on my ViewModel, the original converter worked and didn’t seem to conflict with DotVVM’s.

It’s still odd to me that $type would be missing when I was still using the dictionary and specifically, the addition of my converter would remove $type. Either way, I’m in a good state now and I appreciate your time in this matter.

1 Like