If you absolutely want to avoid DTOs and [JsonIgnore], and really want to use a custom attribute, you would probably have to use some reflection. I'll present a solution which is far from being the best option, but it can give you some ideas.
First, create a custom attribute to mark the properties of your model which aren't supposed to be displayed via your public API:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
class DontSendInPublicApiAttribute : Attribute { }
You'd have to create a method for "erasing" the data on the object's properties which you do not want to display.
public static void RemoveSecretData(object obj)
{
// Retrieve all public instance properties defined for the object's type and marked with [DontSendInPublicApi]
var propertiesToHide = obj.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(p => p.GetCustomAttribute<DontSendInPublicApiAttribute>() != null);
foreach (var prop in propertiesToHide)
{
// Set all of these properties in the given object to their default values.
// VALUE TYPES (ints, chars, doubles, etc.) will be set to default(TheTypeOfValue), by calling Activator.CreateInstance(TheTypeOfValue).
// REFERENCE TYPES will simply be set to null.
var propertyType = prop.PropertyType;
if (propertyType.IsValueType)
prop.SetValue(obj, Activator.CreateInstance(prop.PropertyType));
else
prop.SetValue(obj, null);
}
}
Then apply the attribute to any fields in your model which you wish to hide:
class Person
{
public string Name { get; set; }
public int Age { get; set; }
[DontSendInPublicApi]
public string Occupation { get; set; }
[DontSendInPublicApi]
public int Salary { get; set; }
}
And here's an example of how to call it:
var person = new Person() { Name = "John", Age = 29, Occupation = "Engineer", Salary = 200000 };
RemoveSecretData(person);
After RemoveSecretData(person) has been executed, you'll have the Occupation and Salary properties of the person object set to null and 0, respectivelly.
Notes about this solution:
- Works only on properties. You'd have to modify the
RemoveSecretData() method to also work with fields, if necessary.
- Doesn't recursivelly access the objects graph. If your object references another object with some property marked with
[DontSendInPublicApi], this property will not be hidden. You'd have to modify the RemoveSecretData() method to perform recursive calls on deeper objects, if necessary. Watch out for circular references if you plan on doing so.
- Hidden properties will still be shown on output JSON, but value-typed properties will always present a value of
0 (zero) and reference-typed properties will always present a value of null.