Polymorphic serialization with front end

I am struggling to figure out how to handle my current use case.

On the backend I have this class I use to generate schema for OpenAI requests:

[DataContract]
public class OpenAiJsonSchema : AuditBase
{
    [AutoIncrement]
    public int Id { get; set; }
    [ForeignKey(typeof(Project))]
    public Guid ProjectId { get; set; }
    public required string Name { get; set; }
    [DataMember]
    public required string Description { get; set; }
    [DataMember]
    [PgSqlJson]
    public required List<OpenAiJsonSchemaField> Fields { get; set; }


}
[DataContract]
public abstract class OpenAiJsonSchemaField
{
    [DataMember]
    public required string Name { get; set; }
    [DataMember]
    public required string Description { get; set; }
    [DataMember]
    public bool IsOptional { get; set; } = false;
}

[DataContract]
public class StringField : OpenAiJsonSchemaField
{
    [DataMember]
    public int? MinLength { get; set; }
    [DataMember]
    public int? MaxLength { get; set; }
}

[DataContract]
public class NumberField : OpenAiJsonSchemaField
{
    [DataMember]
    public decimal? Minimum { get; set; }
    [DataMember]
    public decimal? Maximum { get; set; }
    [DataMember]
    public bool ExclusiveMinimum { get; set; }
    [DataMember]
    public bool ExclusiveMaximum { get; set; }
    [DataMember]
    public decimal? MultipleOf { get; set; }
}

[DataContract]
public class IntegerField : OpenAiJsonSchemaField
{
    [DataMember]
    public int? Minimum { get; set; }
    [DataMember]
    public int? Maximum { get; set; }
    [DataMember]
    public bool ExclusiveMinimum { get; set; }
    [DataMember]
    public bool ExclusiveMaximum { get; set; }
    [DataMember]
    public int? MultipleOf { get; set; }
}

[DataContract]
public class BooleanField : OpenAiJsonSchemaField { }

[DataContract]
public class ArrayField : OpenAiJsonSchemaField
{
    [DataMember]
    public required OpenAiJsonSchemaField Items { get; set; }
    [DataMember]
    public int? MinItems { get; set; }
    [DataMember]
    public int? MaxItems { get; set; }
    [DataMember]
    public bool UniqueItems { get; set; }
}

[DataContract]
public class ObjectField : OpenAiJsonSchemaField
{
    [DataMember]
    public required List<OpenAiJsonSchemaField> Fields { get; set; }
}

[DataContract]
public class EnumField : OpenAiJsonSchemaField
{
    [DataMember]
    public required List<string> EnumValues { get; set; }
}

The List<OpenAiJsonSchemaField> Fields is a common base class as each field type has different additional properties.

This serializes fine to OrmLite DB as the types get stored and re-serialized but I can’t figure out how to make this work with the front end (React TS).

I need to build a front end where a user can create and edit these objects then save them.

I can get the API to return the type information like so:

return new HttpResult(areaSchema) {
ResultScope = () => 
    JsConfig.With(new ServiceStack.Text.Config { IncludeTypeInfo = true })
};

This lets me check for the type property on the front end but how do I pass it back to the API so it get deserialized properly so it can be saved into the DB? Even If I post back the exact json with the embedded types I get a serialization error.

How can I edit/create this object on front end and pass it to the API?

I avoid late bound object, interface and polymorphic type properties for issues like this. You would need to construct a JSON payload for the specific serializer that your App is configured with, e.g. ServiceStack.Text which requires storing the full C# Type Name to deserialize into its serializer-specific __type property, which should be the first property defined for the type.

But I personally avoid it as customizing JSON for a specific JSON Serializer limits its interoperability with other languages and even .NET Serializers, I’ve talked about different ways on avoiding polymorphism previously on this forum.

1 Like

Thanks. I just realized it wasn’t working because I had the base class as abstract which was causing the serialization to fail.

I agree this isn’t ideal. Only alternative I can think of is to add a type property and a Dictionary<string, object> to OpenAiJsonSchemaField and then manually deserialize from that. That is probably an easier to document approach.