Closed warriordog closed 1 year ago
Proposed new API for TrySerializeDelegate
/ TryDeserializeDelegate
. This introduces a metadata object to allow backwards-compatible changes in the future. This loses support for custom conversion into a non-object type. That will need to be a new attribute.
/// <summary>
/// Context for a particular JSON conversion operation.
/// Singleton - created once and reused for entire graph.
/// </summary>
public class ConversionMetadata
{
/// <summary>
/// JSON serializer options in use for the conversion.
/// MUST be passed on - do not assume default values!
/// </summary>
public required JsonSerializerOptions JsonSerializerOptions { get; init; }
}
/// <summary>
/// Context for a particular JSON serialization operation.
/// Singleton - created once and reused for entire graph.
/// </summary>
public class SerializationMetadata : ConversionMetadata
{
/// <summary>
/// JSON node options in use for the conversion.
/// MUST be passed on - do not assume default values!
/// </summary>
public required JsonNodeOptions JsonNodeOptions { get; init; }
}
/// <summary>
/// Context for a particular JSON deserialization operation.
/// Singleton - created once and reused for entire graph.
/// </summary>
public class DeserializationMetadata : ConversionMetadata
{
/// <summary>
/// TypeMap of the object being converted.
/// </summary>
public required TypeMap TypeMap { get; init; }
/// <summary>
/// JSON-LD context in effect for this conversion.
/// Will always be present, even if not included in the JSON.
/// </summary>
public required JsonLDContext LDContext { get; init; }
}
/// <summary>
/// Serialize the type into JSON.
/// </summary>
/// <param name="obj">Object to convert</param>
/// <param name="meta">Context for the conversion</param>
/// <param name="node">Node to write values into</param>
/// <returns>Return true on success, or false to fall back on default logic.</returns>
/// <typeparam name="T">Type of object to convert. Must derive from <see cref="ASBase"/>.</typeparam>
public delegate bool TrySerializeDelegate<in T>(T obj, SerializationMetadata meta, JsonObject node)
where T : ASBase;
/// <summary>
/// Deserialize the type from JSON.
/// </summary>
/// <param name="element">Element containing JSON data for this object</param>
/// <param name="meta">Context for the conversion</param>
/// <param name="obj">Object constructed by the converter</param>
/// <returns>Return true on success, or false to fall back on default logic.</returns>
/// <typeparam name="T">Type of object to convert. Must derive from <see cref="ASBase"/>.</typeparam>
public delegate bool TryDeserializeDelegate<T>(JsonElement element, DeserializationMetadata meta, out T? obj)
where T : ASBase;
Proposed NEW attribute to remap a type on deserialization:
/// <summary>
/// Selects a more narrow type to convert instead of the containing type.
/// This will be called on deserialization.
/// The returned type MUST be or derive from the containing type.
/// </summary>
/// <param name="element">Element containing JSON data for this object</param>
/// <param name="meta">Context for the conversion</param>
/// <returns>Type of object to convert</returns>
public delegate Type NarrowTypeDelegate(JsonElement element, DeserializationMetadata meta);
/// <summary>
/// Indicates that the target method should be called to narrow the type of this object before deserialization.
/// Only valid on subtypes of <see cref="ASBase"/>.
/// </summary>
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
[MeansImplicitUse]
public sealed class NarrowJsonTypeAttribute : Attribute
{
/// <summary>
/// Name of the method that will narrow the type of this object
/// Must be public, static, and conform to the signature of <see cref="NarrowTypeDelegate"/>.
/// </summary>
public string MethodName { get; }
public NarrowJsonTypeAttribute(string methodName) => MethodName = methodName;
}
Proposed NEW attribute to special-convert the entire graph into a JSON value type:
/// <summary>
/// Serialize the type into a JSON value.
/// This will supersede all conversion for the ENTIRE object graph, so use it carefully!
/// </summary>
/// <param name="obj">Object to convert</param>
/// <param name="meta">Context for the conversion</param>
/// <param name="node">Node to use as value for the object</param>
/// <returns>Return true on success, or false to fall back on default logic.</returns>
/// <typeparam name="T">Type of object to convert. Must derive from <see cref="ASBase"/>.</typeparam>
public delegate bool TrySerializeIntoValueDelegate<in T>(T obj, SerializationMetadata meta, [NotNullWhen(true)] out JsonValue? node)
where T : ASBase;
/// <summary>
/// Indicates that the target method should be called to serialize this type into a non-object value.
/// Only valid on subtypes of <see cref="ASBase"/>.
/// </summary>
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
[MeansImplicitUse]
public sealed class CustomJsonValueSerializerAttribute : Attribute
{
/// <summary>
/// Name of the method that can serialize this type.
/// Must be public, static, and conform to the signature of <see cref="TrySerializeIntoValueDelegate{T}"/> where T is substituted for the type.
/// </summary>
public string MethodName { get; }
public CustomJsonValueSerializerAttribute(string methodName) => MethodName = methodName;
}
Research - design new CustomJson(De)Serializer callback. We need to pass the TypeMap through to custom logic, and/or create a new callback to override the type mapping.
More info - https://github.com/warriordog/ActivityPubSharp/issues/74#issuecomment-1660685784