C# - Does it make sense to have an empty interface with a class level attribute to specify common behavior?

237 views Asked by At

I have several classes that serve as data transfer objects (in C#). These classes are all serialized using JSON.Net to be sent to an API. Unfortunately, the property names specified in the API don't have standardized naming conventions. Some properties use camel casing, some use snake casing, some all lowercase, and so on. So, I need to account for this when serializing the DTOs to JSON. Also, when properties are null, I want them to be excluded from the serialized JSON.

So, here was my solution. I created an empty interface called ISerializableDto and added class level attributes specifying that properties should be excluded when null and the default property naming strategy is snake casing.

[JsonObject(
    NamingStrategyType = typeof(Newtonsoft.Json.Serialization.SnakeCaseNamingStrategy), 
    ItemNullValueHandling = NullValueHandling.Ignore)]
public interface ISerializableDto
{
}

All of my DTO classes inherit from this interface. When the naming convention for a property in one of the DTO classes is not snake cased, I just specify the property name explicitly through a property level attribute.

public class ExampleDto : ISerializableDto
{
    public string SomeRandomPropertyName { get; set; } // This will serialize to "some_random_property_name"

    [JsonProperty(PropertyName = "someRandomCamelCasedPropertyName")]
    public string SomeRandomCamelCasedPropertyName { get; set; } // This will serialize to "someRandomCamelCasedPropertyName"
}

This all works fine. I can do a simple JsonConvert.SerializeObject(myDtoObject) and it behaves exactly as I intended. The property names are snake cased unless otherwise specified and null properties are excluded. But, I'm not confident this is the best way to go about doing this.

Does it make sense to have an empty interface with a class level attribute to specify common behavior?

I could remove the interface entirely and put the class level attributes on each DTO class. But, someone might miss it if they need to add a new DTO class later on. Or, I could even specify these behaviors in the JsonConvert.SerializeObject call. But my worry there is that some of the naming behavior is specified in the DTO class, and some is specified externally.

Is there some better way of doing this?

1

There are 1 answers

0
sjb-sjb On

I would probably do this by creating an attribute [SerializableDto] that derives from JsonProperty and contains the settings that you want. Since all the rest of your specifiers (like for the property names) are done with attributes it seems more consistent. If you put it in an interface you create a more complex situation where some of the specification is done by implementing an interface while other related specification is done using attributes.

Have not checked whether or not JsonProperty is sealed. If it is then your situation is trickier. You might still be able to do it though using an attribute that dynamically attaches the Json attribute using TypeDescriptor.AddAttributes.