lucidiot/HabitSharp
lucidiot
/
HabitSharp
Archived
1
0
Fork 0

Notification class selection based on type property

This commit is contained in:
Lucidiot 2018-10-07 18:20:50 +02:00
parent 920187e876
commit 70d1b79a14
No known key found for this signature in database
GPG Key ID: AE3F7205692FA205
4 changed files with 67 additions and 6 deletions

View File

@ -1,9 +1,8 @@
using System;
namespace HabitSharp {
namespace HabitSharp.Notifications {
public class Notification {
public Guid Id { get; set; }
public bool Seen { get; set; }
public string Type { get; set; }
}
}

View File

@ -4,6 +4,9 @@ using RestSharp.Serializers;
using RestSharp.Deserializers;
namespace HabitSharp.Serialization {
/// <summary>
/// A RestSharp Serializer and Deserializer that uses Newtonsoft.Json
/// </summary>
public class NewtonsoftJsonSerializer : ISerializer, IDeserializer
{
private Newtonsoft.Json.JsonSerializer serializer;
@ -44,9 +47,14 @@ namespace HabitSharp.Serialization {
}
}
public static NewtonsoftJsonSerializer Default =>
new NewtonsoftJsonSerializer(new Newtonsoft.Json.JsonSerializer() {
NullValueHandling = NullValueHandling.Ignore,
});
public static NewtonsoftJsonSerializer Default {
get {
var ser = new Newtonsoft.Json.JsonSerializer() {
NullValueHandling = NullValueHandling.Ignore,
};
ser.Converters.Add(new NotificationConverter());
return new NewtonsoftJsonSerializer(ser);
}
}
}
}

View File

@ -0,0 +1,47 @@
using System;
using System.Linq;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using HabitSharp.Notifications;
/// <summary>
/// A converter to create subclasses of Notification when possible to fetch more information.
/// If a notification is of an unknown type, will create a simple Notification class.
/// </summary>
/// <remarks>Serialization to JSON is not supported; this converter can only perform deserialization.</remarks>
public class NotificationConverter : JsonConverter
{
/// <summary>
/// Get a class derived from Notification is there is one matching the given notification type, or return Notification if none is found.
/// </summary>
/// <param name="notificationType">The notification type string from JSON data.</param>
/// <returns>System.Type for the Notification class or one of its derived classes</returns>
public Type FindNotificationSubclass(string notificationType) {
// Find all Notification subclasses in the same assembly
return typeof(Notification).Assembly.GetTypes()
.Where(t => t.IsSubclassOf(typeof(Notification)))
.SingleOrDefault(
// Find the only one subclass that has a NotificationType attribute with the right type string
t => Attribute.GetCustomAttributes(t, typeof(NotificationTypeAttribute)).Any(
attr => ((NotificationTypeAttribute)attr).TypeString.Equals(notificationType, StringComparison.InvariantCultureIgnoreCase)
)
) ?? typeof(Notification); // Default to the Notification class
}
public override bool CanConvert(Type objectType) => objectType.IsSubclassOf(typeof(Notification)) || objectType == typeof(Notification);
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject jo = JObject.Load(reader);
Type noteClass = FindNotificationSubclass((string)jo["type"]);
// Dirty hack to use a Type variable as a type in a generic method
// TODO: There probably is a nicer way to do this.
var method = typeof(JObject).GetMethod("ToObject").MakeGenericMethod(new Type[] { noteClass });
return method.Invoke(jo, new object[] { serializer });
}
public override bool CanWrite => false;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException();
}

View File

@ -0,0 +1,7 @@
using System;
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class NotificationTypeAttribute : Attribute {
public readonly string TypeString;
public NotificationTypeAttribute(string type) => this.TypeString = type;
}