Json.NET - Conditional Property Serialization

Häufig müssen Daten in Abhängigkeit der Benutzerrechte ein- bzw. ausgeblendet werden. Werden diese Daten über ein Web API konsumiert liegen sie oft im JSON Format vor. Dabei kommt es vor, dass Daten geladen werden, die für den Benutzer nicht sichtbar sind, aber mit Hilfe von entsprechenden Tools wie Fiddler problemlos sichtbar gemacht werden können.

JSON.Net bietet zwar mit Conditional Property Serialization bereits eine Möglichkeit ein Property in Abhängigkeit einer beliebigen Bedingung von der Serialisierung auszunehmen, die Steuerung über ein Attribut ist aber die elegantere und sichere Variante. Das Attribut besitzt nur eine Methode IsAdmin() mit der das aktuelle Principal-Objekt abgefragt wird.

[AttributeUsage(AttributeTargets.All, Inherited = true, AllowMultiple = false)]
public class JsonAdminOnlyAttribute : Attribute
{
    public bool IsAdmin()
    {
        return ((MyCustomPrincipal)Thread.CurrentPrincipal).IsAdmin();
    }
}

Properties die nur für Administratoren sichtbar sein sollen, können nun einfach mit dem JsonAdminOnlyAttribute dekoriert werden

public sealed class MyDTO
{
    public int Id { get; set; }
    public String Text { get; set; }

    [JsonAdminOnly]
    public String SuperSecretValue { get; set; }
}

Über einen eigenen ContractResolver können wir zur Laufzeit über das JsonAdminOnlyAttribute die IsAdmin() Methode abfragen.

public class CustomContractResolver : CamelCasePropertyNamesContractResolver
{
    protected override JsonProperty CreateProperty(System.Reflection.MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty property = base.CreateProperty(member, memberSerialization);
        var adminOnly = member.GetCustomAttributes(false).OfType().FirstOrDefault();
        if (adminOnly != null)
        {
            property.ShouldSerialize = (obj) => { return adminOnly.IsAdmin(); };
        }

        return property;
    }
}

Abschliessend muss der CustomContractResolver beim Start der Applikation registriert werden, zB in der Global.asax bei ASP.NET Webapplikationen.

var jsonFormatter = HttpConfiguration.Formatters.JsonFormatter;
jsonFormatter.SerializerSettings.ContractResolver = new CustomContractResolver();

Nun werden alle Properties die mit dem JsonAdminOnlyAttribute Attribut dekoriert sind nur noch serialisiert wenn der aktuelle Benutzer Admin-Rechte besitzt. Als Test kann natürlich auch ein simples Konsolen-Programm verwendet werden

class Program
{
    static void Main(string[] args)
    {
        MyDTO dto = new MyDTO();
        dto.Id = 1234;
        dto.Text = "Hello World";
        dto.SuperSecretValue = "xxxx";

        string json = JsonConvert.SerializeObject(dto, new JsonSerializerSettings() {
            ContractResolver = new CustomContractResolver(),
            Formatting = Formatting.Indented
        });
    }
}