0

This is the code I would like to write at the end:

var notification = NotificationFactory.FromJson(jsonString);

if (notification is UserUpdateProfileNotification)
{
  // notification is of type UserUpdateProfileNotification so I can access notification.Payload.AccoundId
}

This is what I tried so far:

public abstract class Notification<T>
{
    public string Type { get; set; }
    public T Payload { get; set; }
}


public class UserUpdateProfileNotificationPayload
{
    public Guid AccountId { get; set; }
}

public class UserUpdateProfileNotification: Notification<UserUpdateProfileNotificationPayload>
{
    public UserUpdateProfileNotification(UserUpdateProfileNotificationPayload payload)
    {
        Type = "USER_UPDATE_PROFILE";
        Payload = payload;
    }
}

public static class NotificationFactory
{
    public static Notification<object> FromJson(string notification)
    {
        var parsedNotif = JsonSerializer.Deserialize<Notification<object>>(notification);

        if (parsedNotif.Type == "USER_UPDATE_PROFILE")
        {
             var concreteType = JsonSerializer.Deserialize<UserUpdateProfileNotification>(notification);
            return new UserUpdateProfileNotification(concreteType.Payload);
        }

        throw new NotImplementedException();
    }
}

The error I got:

Cannot implicitly convert type 'UserUpdateProfileNotification' to 'Notification'

For me, converting Notification<x> to Notification<object> with x inherit from object should be "automatic". But it's not. What's the best way to express this idea in C# (10) ?

Titouan56
  • 6,932
  • 11
  • 37
  • 61
  • Your "code id like to write" does not include the generic type parameter that your example of what youve tried requires `FromJson`. And therein lies the problem - even if you could write the code you wanted to write up top, you wouldnt be able to do anything useful with it as it will be completely untyped. At very least tyou'd need `var notification = NotificationFactory.FromJson(jsonString);` and then your problem is solved - except I see why it's not! Your type is decided by a `Type` in the json. Catch-22 or chicken/egg – Jamiec Dec 14 '21 at 13:32
  • oh sorry, I made a mistake in the code, I edited it. I want to return a Notification (or anything else I can pattern match on it) – Titouan56 Dec 14 '21 at 13:33
  • ok, but problem still exists to an extent. `payload.ToString()` wont return a JSON string. You could choose to just re-serialize and then deserialize it I guess – Jamiec Dec 14 '21 at 13:36
  • Yes, I didn't even notice that bug. I cannot make it to compile because of the type problem: "return new UserUpdateProfileNotification(payload);". I (probably) fixed the bug in deserializing with the right type – Titouan56 Dec 14 '21 at 13:37
  • The problem you have is `UserUpdateProfileNotification` is NOT a `Notification` nor can it ever be. So you cant return the former as the latter. Your only real option is hand parsing the JSON but you're still not going to be able to do anything sensible with the result except pattern match it which kind of defeats the purpose of having a general purpose `ParseJson` method – Jamiec Dec 14 '21 at 13:50
  • This might help: https://stackoverflow.com/questions/40630629/deserialize-object-based-on-value-type-in-property – Jamiec Dec 14 '21 at 13:52
  • And this: https://stackoverflow.com/questions/38357864/how-to-conditionally-deserialize-json-object-based-on-another-json-property – Jamiec Dec 14 '21 at 13:52
  • When you find yourself checking for a derived type in a situation like this it's a sign of a bigger design problem. Since we don't know what you're actually doing with this type that's different from others, it's hard to say what you should be doing instead, but odds are you should be providing virtual members on `Notification` that can do the appropriate thing for each specific type of notification. For example, if you want to have a different log message for different types of notifications they should each know how to log themselves, or generate a `Message` property, etc. – Servy Dec 14 '21 at 15:53

1 Answers1

-1

I got something to work (I use dynamic instead of Notification and pattern matching work)

Factory

public class NotificationFactory
{
    public static dynamic FromJson(string notification)
    {
        var parsedNotif = JsonSerializer.Deserialize<Notification<dynamic>>(notification);

        if (parsedNotif.Type == "USER_UPDATE_PROFILE")
        {
            var concreteType = JsonSerializer.Deserialize<UserUpdateProfileNotification>(notification);
            return new UserUpdateProfileNotification(concreteType.Payload);
        }

        throw new NotImplementedException();
    }
}

How I use:

  var notification = NotificationFactory.FromJson(item);

  if (notification is UserUpdateProfileNotification notif)
  {
      log.LogInformation($"{notif.Payload.AccountId}");
  }

Double parsing is not optimal but it is acceptable in my case

Titouan56
  • 6,932
  • 11
  • 37
  • 61