nadako / TinkStateSharp

Handle those pesky states, now in C#
https://nadako.github.io/TinkStateSharp/
The Unlicense
43 stars 1 forks source link

explore what's required for serialization #32

Open nadako opened 3 months ago

nadako commented 3 months ago

We might need to expose some APIs to provide a way to (de-)serialize State. With Json.NET, it doesn't actually need any changes to the library, here's a quick and dirty implementation of a JsonConverter:

class StateJsonConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        if (objectType.IsGenericType)
        {
            // field type definition for deserialization - the interface directly
            if (objectType.GetGenericTypeDefinition() == typeof(State<>))
            {
                return true;
            }

            // run-time object type for serialization - the implementation
            foreach (var iface in objectType.GetInterfaces())
            {
                if (iface.IsGenericType && iface.GetGenericTypeDefinition() == typeof(State<>))
                {
                    return true;
                }
            }
        }
        return false;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        GetConverter(value.GetType()).WriteJson(writer, value, serializer);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        return GetConverter(objectType).ReadJson(reader, existingValue, serializer);
    }

    StateConverter GetConverter(Type stateType)
    {
        // TODO: cache converters
        var rt = typeof(StateConverter<>).MakeGenericType(stateType.GetGenericArguments()[0]);
        return (StateConverter)Activator.CreateInstance(rt, false);
    }
}

abstract class StateConverter
{
    public abstract void WriteJson(JsonWriter writer, object value, JsonSerializer serializer);

    public abstract object ReadJson(JsonReader reader, object existingValue, JsonSerializer serializer);
}

class StateConverter<T> : StateConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        WriteJsonInternal(writer, (State<T>)value, serializer);
    }

    public override object ReadJson(JsonReader reader, object existingValue, JsonSerializer serializer)
    {
        return ReadJsonInternal(reader, existingValue, serializer);
    }

    void WriteJsonInternal(JsonWriter writer, State<T> state, JsonSerializer serializer)
    {
        serializer.Serialize(writer, state.Value);
    }

    State<T> ReadJsonInternal(JsonReader reader, object existingValue, JsonSerializer serializer)
    {
        var stateValue = serializer.Deserialize<T>(reader);
        if (existingValue is State<T> existingState)
        {
            existingState.Value = stateValue;
            return existingState;
        }
        return Observable.State(stateValue);
    }
}