Search code examples
c#json.netjson.netoutbox-pattern

Getting default values using TypeNameHandling in Newtonsoft JSON


I'm doing a basic serialize / deserialize with $type included to implement the outbox pattern. however I'm getting the default values.

Here's the code serializing :

JsonConvert.SerializeObject(domainEvent, 
new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }

and for de-serializing

var domainEvent = JsonConvert.DeserializeObject<IDomainEvent>(
    serializedDomainEvent,
    new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All});

here's the domain event i'm trying to serialize

public record SubscriptionSet(Subscription subscription) : IDomainEvent

and lastly the subscription class

public class Subscription : AggregateRoot
{
    private readonly List<Alert> _alerts = new();

    public Guid UserId { get; private set; }
    public SubscriptionTier subscriptionTier { get; private set; }
    public SubscriptionPlan subscriptionPlan { get; private set; }
    public DateOnly StartDate { get; private set; }
    public DateOnly EndDate { get; private set; }
    public bool IsVaild { get; private set; }
    public decimal ComulativeTradingVolume24H { get; private set; }

    public IReadOnlyList<Alert> Alerts => _alerts.ToList();
}

as

The serialization part is working properly but i'm messing up the deserialization part.
This is the serialization output :

{"$type":"Domain.Subscriptions.Events.SubscriptionSet, Domain","subscription":{"$type":"Domain.Subscriptions.Subscription, Domain","UserId":"e09cf0c7-5cc7-4fd7-b899-ac9dc74a70a4","subscriptionTier":1,"subscriptionPlan":1,"StartDate":"2024-12-05","EndDate":"2025-03-05","IsVaild":true,"ComulativeTradingVolume24H":0.0,"Alerts":{"$type":"System.Collections.Generic.List`1[[Domain.Users.Alert, Domain]], System.Private.CoreLib","$values":[]},"Id":"ad35aeb0-c2dc-4ac2-a19f-e7fe9fcbc0c9"}}

Solution

  • As Told by the awesome people in the Comments, Private setters was the problem

    public Guid UserId { get; private set; }
    

    in order for this to work, either change setter to public or user [JsonProperty]

    [JsonProperty]
    public Guid UserId { get; private set; }
    

    or if you don't like to decorate your classes with attributes you can, implement a custom contract resolver

    public class PrivateResolver : DefaultContractResolver
    {
        protected override JsonProperty CreateProperty(MemberInfo member,
              MemberSerialization memberSerialization)
        {
            JsonProperty prop =  base.CreateProperty(member, memberSerialization);
    
            if (!prop.Writable)
            {
                var property = member as PropertyInfo;
    
                bool hasPrivateSetters = property?.GetSetMethod(true) != null;
    
                prop.Writable = hasPrivateSetters;
            }
    
            return prop;
        }
    }
    

    and in your de-serialization code do this

    var domainEvent = JsonConvert.DeserializeObject<IDomainEvent>(
        outboxMessage.Content,
        new JsonSerializerSettings { 
            TypeNameHandling = TypeNameHandling.All , 
            ContractResolver = new PrivateResolver() });