Code/AltCurve.JsonConverter.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace AltCurves;
public readonly partial record struct AltCurve
{
/// <summary>
/// Serialization for AltCurve
/// </summary>
public class JsonConverter : JsonConverter<AltCurve>
{
public const uint SERIALIZE_VERSION = 1;
private const string TokenName_Version = "_ace_v";
private const string TokenName_PreInfinity = "pri";
private const string TokenName_PostInfinity = "poi";
private const string TokenName_Keys = "keys";
public override AltCurve Read( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options )
{
if ( reader.TokenType != JsonTokenType.StartObject )
throw new JsonException();
uint version = 0;
Extrapolation preInfinity = 0;
Extrapolation postInfinity = 0;
List<Keyframe> keyframes = new();
while ( reader.Read() )
{
if ( reader.TokenType == JsonTokenType.EndObject )
{
if ( version != SERIALIZE_VERSION )
throw new JsonException( $"Error parsing AltCurve json, unsupported version: {version} vs {SERIALIZE_VERSION}" );
#if DEBUG
// Warn in debug mode if the sanitized keyframe differs from what we tried to load
var sanitizedKeyframes = SanitizeKeyframes( keyframes );
if ( !sanitizedKeyframes.SequenceEqual( keyframes ) )
{
// Notify of keyframe removals
foreach ( var removedKey in keyframes.Except( sanitizedKeyframes ) )
{
Log.Error( $"Discarded invalid AltCurve keyframe JSON, no keyframes in a curve may share a time. Removed keyframe: '{removedKey}'." );
}
// Check if remaining keyframes are in the same order (intersection should be equal to sanitized keyframes)
if ( !keyframes.Intersect( sanitizedKeyframes ).SequenceEqual( sanitizedKeyframes ) )
{
Log.Error( "AltCurve keyframe JSON out-of-order, performed fixup." );
}
keyframes = sanitizedKeyframes.ToList();
}
#endif
return new( keyframes, preInfinity, postInfinity );
}
if ( reader.TokenType == JsonTokenType.PropertyName )
{
string propertyName = reader.GetString();
reader.Read();
switch ( propertyName )
{
case TokenName_Version:
version = reader.GetUInt32();
break;
case TokenName_PreInfinity:
preInfinity = JsonSerializer.Deserialize<AltCurve.Extrapolation>( ref reader, options );
break;
case TokenName_PostInfinity:
postInfinity = JsonSerializer.Deserialize<AltCurve.Extrapolation>( ref reader, options );
break;
case TokenName_Keys:
keyframes = JsonSerializer.Deserialize<List<Keyframe>>( ref reader, options );
break;
default:
throw new JsonException( $"Unknown property: {propertyName}" );
}
}
}
throw new JsonException( "Unexpected end of JSON." );
}
public override void Write( Utf8JsonWriter writer, AltCurve val, JsonSerializerOptions options )
{
writer.WriteStartObject();
writer.WritePropertyName( TokenName_Version );
writer.WriteNumberValue( SERIALIZE_VERSION );
if ( val.PreInfinity != default( AltCurve ).PreInfinity )
{
writer.WritePropertyName( TokenName_PreInfinity );
JsonSerializer.Serialize( writer, val.PreInfinity, options );
}
if ( val.PostInfinity != default( AltCurve ).PostInfinity )
{
writer.WritePropertyName( TokenName_PostInfinity );
JsonSerializer.Serialize( writer, val.PostInfinity, options );
}
if ( !val.Keyframes.IsDefaultOrEmpty )
{
writer.WritePropertyName( TokenName_Keys );
JsonSerializer.Serialize( writer, val.Keyframes, options );
}
writer.WriteEndObject();
}
}
}