TypeNameHandling with attribute in class

I am using TypeNameHandling to serialize and deserialize a list of derived class in json. It works great with property and attributeJsonProperty

public abstract class Animal
{
    public bool CanFly { get; set;}
}

public class FlyingAnimal : Animal
{
    public FlyingAnimal() { this.CanFly = true; }
}

public class SwimmingAnimal : Animal
{
    public SwimmingAnimal() { this.CanFly = false; }
}

public class World
{
    public World() { 
        this.Animals = new List<Animal>(); 
        this.Animals.Add(new FlyingAnimal());
        this.Animals.Add(new SwimmingAnimal());
    }

    [JsonProperty(ItemTypeNameHandling = TypeNameHandling.Auto)]
    public List<Animal> Animals { get; set; }
}

Now I need the WebAPI to return a list of the derived class:

[RoutePrefix("Animals")]
public class AnimalsController : ApiController
{
    public List<Animal> Get()
    {
        List<Animal> animals = new List<Animal>(); 
        animals.Add(new FlyingAnimal());
        animals.Add(new SwimmingAnimal());
        return animals;
    }
}

Is there an attribute to include a base class for including a type in serialization? I tried without success:

[JsonObject(ItemTypeNameHandling = TypeNameHandling.Auto)]
public abstract class Animal

I know I can change JsonSerializerSettings, but I need a solution with an attribute in the base class

+3
source share
2 answers

There is no functionality in Json.NET to do this out of the box, but you can do this with custom contract resolution :

[AttributeUsage(AttributeTargets.Class| AttributeTargets.Interface, AllowMultiple = false, Inherited = false)]
public class AddJsonTypenameAttribute : System.Attribute
{
}

public class AddJsonTypenameContractResolver : DefaultContractResolver
{
    // As of 7.0.1, Json.NET suggests using a static instance for "stateless" contract resolvers, for performance reasons.
    // http://www.newtonsoft.com/json/help/html/ContractResolver.htm
    // http://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_Serialization_DefaultContractResolver__ctor_1.htm
    // "Use the parameterless constructor and cache instances of the contract resolver within your application for optimal performance."
    // See also https://stackoverflow.com/questions/33557737/does-json-net-cache-types-serialization-information
    static AddJsonTypenameContractResolver instance;

    // Explicit static constructor to tell C# compiler not to mark type as beforefieldinit
    static AddJsonTypenameContractResolver() { instance = new AddJsonTypenameContractResolver(); }

    public static AddJsonTypenameContractResolver Instance { get { return instance; } }

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        return base.CreateProperty(member, memberSerialization)
            .ApplyAddTypenameAttribute();
    }

    protected override JsonArrayContract CreateArrayContract(Type objectType)
    {
        return base.CreateArrayContract(objectType)
            .ApplyAddTypenameAttribute();
    }
}

public static class ContractResolverExtensions
{
    public static JsonProperty ApplyAddTypenameAttribute(this JsonProperty jsonProperty)
    {
        if (jsonProperty.TypeNameHandling == null)
        {
            if (jsonProperty.PropertyType.GetCustomAttribute<AddJsonTypenameAttribute>(false) != null)
            {
                jsonProperty.TypeNameHandling = TypeNameHandling.All;
            }
        }
        return jsonProperty;
    }

    public static JsonArrayContract ApplyAddTypenameAttribute(this JsonArrayContract contract)
    {
        if (contract.ItemTypeNameHandling == null)
        {
            if (contract.CollectionItemType.GetCustomAttribute<AddJsonTypenameAttribute>(false) != null)
            {
                contract.ItemTypeNameHandling = TypeNameHandling.All;
            }
        }
        return contract;
    }
}

Then apply it to your interfaces or base types as follows:

[AddJsonTypename]
public interface IAnimal
{
    bool CanFly { get; }
}

[AddJsonTypename]
public abstract class Animal : IAnimal
{
    public bool CanFly { get; set; }
}

, Inherited = false. , List<Animal> , List<FlyingAnimal> . , . , ., , .

, -API, ., , Web API 2: JSON camelCased, -. Newtonsoft:

TypeNameHandling , JSON . SerializationBinder , None.

, , . TypeNameHandling Newtonsoft Json, Json.NET -API, blackhat https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-JSON-Attacks-wp.pdf.

+4

, , :

[JsonArray(ItemTypeNameHandling = TypeNameHandling.Auto)]
public class AnimalList : List<Animal>
{ }

:

[RoutePrefix("Animals")]
public class AnimalsController : ApiController
{
    public List<Animal> Get()
    {
        List<Animal> animals = new AnimalList(); 
        animals.Add(new FlyingAnimal());
        animals.Add(new SwimmingAnimal());
        return animals;
    }
}
+1

Source: https://habr.com/ru/post/1691901/


All Articles