jsonsubtypes

repository·master·Indexed 19 days ago

https://github.com/manuc66/jsonsubtypes

A library for deserializing JSON objects into specific subtypes using a type discriminator property or the presence of specific properties. It provides attribute-based configuration via [JsonSubtypes.KnownSubType] and [JsonSubtypes.KnownSubTypeWithProperty], as well as programmatic configuration through JsonSubtypesConverterBuilder and JsonSubtypesWithPropertyConverterBuilder for use in JsonSerializerSettings.

Tokens
2K
Snippets
5
Records
7
Agent score
16%

What's inside jsonsubtypes

  1. Deserialize objects with custom type mapping using attributes

    master

    To map specific discriminator values to specific subtypes, use the [JsonSubtypes.KnownSubType] attribute on the base class. This works with various value types, including string, enum, or int.

    Example: Mapping a Sound property to Dog or Cat subtypes.

    [JsonConverter(typeof(JsonSubtypes), "Sound")]
    [JsonSubtypes.KnownSubType(typeof(Dog), "Bark")]
    [JsonSubtypes.KnownSubType(typeof(Cat), "Meow")]
    public class Animal
    {
        public virtual string Sound { get; }
        public string Color { get; set; }
    }
    
    public class Dog : Animal
    {
        public override string Sound { get; } = "Bark";
        public string Breed { get; set; }
    }
    
    public class Cat : Animal
    {
        public override string Sound { get; } = "Meow";
        public bool Declawed { get; set; }
    }
    
    // Usage
    var animal = JsonConvert.DeserializeObject<Animal>("{\"Sound\":\"Bark\",\"Breed\":\"Jack Russell Terrier\"}");
  2. Deserialize objects with a custom type property name using attributes

    master

    You can use the [JsonConverter] attribute on a base type or interface to specify which JSON property contains the type discriminator. The second parameter of the attribute defines the name of this property.

    Note: This approach requires that subtypes are in the same assembly as the base type and are either in the same namespace or use a fully qualified type name.

    [JsonConverter(typeof(JsonSubtypes), "Kind")]
    public interface IAnimal
    {
        string Kind { get; }
    }
    
    public class Dog : IAnimal
    {
        public string Kind { get; } = "Dog";
        public string Breed { get; set; }
    }
    
    public class Cat : IAnimal {
        public string Kind { get; } = "Cat";
        public bool Declawed { get; set;}
    }
    
    // Usage
    var animal = JsonConvert.DeserializeObject<IAnimal>("{\"Kind\":\"Dog\",\"Breed\":\"Jack Russell Terrier\"}");
  3. Define a fallback subtype for unknown types

    master

    When deserializing, you can specify a default class to use if the discriminator value does not match any registered subtypes. This is useful for handling unexpected or evolving data schemas without failing the entire deserialization process.

    You can set this using the [JsonSubtypes.FallBackSubType] attribute or the .SetFallbackSubtype() method in the JsonSubtypesConverterBuilder or JsonSubtypesWithPropertyConverterBuilder.

    // Attribute approach
    [JsonConverter(typeof(JsonSubtypes))]
    [JsonSubtypes.KnownSubType(typeof(ConstantExpression), "Constant")]
    [JsonSubtypes.FallBackSubType(typeof(UnknownExpression))]
    public interface IExpression
    {
        string Type { get; }
    }
    
    // Builder approach
    settings.Converters.Add(JsonSubtypesConverterBuilder
        .Of(typeof(IExpression), "Type")
        .SetFallbackSubtype(typeof(UnknownExpression))
        .RegisterSubtype(typeof(ConstantExpression), "Constant")
        .Build());
  4. Configure JsonSubtypes via JsonSubtypesConverterBuilder for properties only in JSON

    master

    If the type discriminator property is not present in your C# class hierarchy (it only exists in the JSON), you must register the converter explicitly in JsonSerializerSettings using JsonSubtypesConverterBuilder.

    To ensure the discriminator is included during serialization, you must call .SerializeDiscriminatorProperty() in the builder chain.

    var settings = new JsonSerializerSettings();
    settings.Converters.Add(JsonSubtypesConverterBuilder
        .Of(typeof(Animal), "Type") // type property is only defined in JSON
        .RegisterSubtype(typeof(Cat), AnimalType.Cat)
        .RegisterSubtype(typeof(Dog), AnimalType.Dog)
        .SerializeDiscriminatorProperty() // ask to serialize the type property
        .Build());
  5. Deserialize objects by property presence using JsonSubtypesWithPropertyConverterBuilder

    master

    You can determine a subtype based on whether a specific property is present in the JSON object, rather than relying on a dedicated discriminator field.

    This can be achieved via attributes using [JsonSubtypes.KnownSubTypeWithProperty] or via code configuration using JsonSubtypesWithPropertyConverterBuilder.

    Attribute approach:

    [JsonConverter(typeof(JsonSubtypes))]
    [JsonSubtypes.KnownSubTypeWithProperty(typeof(Employee), "JobTitle")]
    [JsonSubtypes.KnownSubTypeWithProperty(typeof(Artist), "Skill")]
    public class Person { ... }
    // Code configuration approach
    settings.Converters.Add(JsonSubtypesWithPropertyConverterBuilder
        .Of(typeof(Person))
        .RegisterSubtypeWithProperty(typeof(Employee), "JobTitle")
        .RegisterSubtypeWithProperty(typeof(Artist), "Skill")
        .Build());
  6. JsonSubtypesConverterBuilder

    master

    A builder used to configure JsonSubtypes converters for use in JsonSerializerSettings. This is required when the discriminator property is not part of the C# class model.

    Methods:

    • Of(Type baseType, string discriminatorPropertyName): Sets the base type and the name of the JSON property used for type discrimination.
    • Of<T>(string discriminatorPropertyName): Generic version of Of.
    • RegisterSubtype(Type subtype, object discriminatorValue): Maps a specific value (string, enum, int, etc.) to a subtype.
    • RegisterSubtype<T>(object discriminatorValue): Generic version of RegisterSubtype.
    • SerializeDiscriminatorProperty(): Instructs the serializer to include the discriminator property in the output JSON.
    • SetFallbackSubtype(Type fallbackType): Defines which type to instantiate if no subtype matches the discriminator.
    • Build(): Returns the configured converter instance.
  7. JsonSubtypesWithPropertyConverterBuilder

    master

    A builder used to configure converters that determine subtypes based on the presence of specific properties in the JSON, rather than a discriminator value.

    Methods:

    • Of(Type baseType): Sets the base type.
    • Of<T>(): Generic version of Of.
    • RegisterSubtypeWithProperty(Type subtype, string propertyName): Maps a subtype to the presence of a specific JSON property.
    • RegisterSubtypeWithProperty<T>(string propertyName): Generic version of RegisterSubtypeWithProperty.
    • SetFallbackSubtype(Type fallbackType): Defines which type to instantiate if no property matches.
    • Build(): Returns the configured converter instance.