How can I use a reserved keyword as an identifier in my JSON model class?

I have never used Web API before, but I need a web service that will accept/return JSON objects and using this seemed like a reasonable thing. It looked pretty simple (if not a bit of overkill for my purposes), but a data structure I need to deal with looks something like:

{
    "values":["foo", "bar"],
    "default":"bar"
}

And so I went to make a Model object:

class DropDownValues {
    public string[] values { get; set; }
    public string default { get; set; }
}

Problem is that default seems to be a protected keyword. There must be some way to get around that, right?


Solution 1:

You can use keywords in C# as identifiers by prepending @ in front of them.

Solution 2:

I would suggest to go different way. Keep your C# object model as much standard as possible (I wouldn't use @ sign and C# keywords as property name).

We can separate the serialized (JSON) world and C# objects - just by using the Json.NET features.

One of the simpliest to use is decoration with Attribute:

[JsonProperty(PropertyName = "default")]
public string DefaultValue { get; set; }

In this case we have to reference Newtonsoft.Json in the project. If it must be POCO, we can introduce CustomResolver derrived from DefaultContractResolver and define these conversions there...

But separation of concern in this case is a bit more pure solution, I would say

EDIT: JSON Contract Resolver draft (see comments)

Important NOTE: Newtonsoft.Json is part of the Web API. Not only it is an open source, but even MS team bet on that as a core JSON serializer.

1) Newtonsoft.Json (as a part of the Web.API) is already installed in your solution. So you do not have to downloaded (nuget) separately. It would always be in your packages folder. So, to use the attribute is just adding the reference. It is there...

2) There is a small draft how to do the attribute stuff, while keeping the POCO. As I've tried explain here: POCO's, behavior and Peristance Igorance, to keep POCO (e.g. we do profit from layered Architecture with NHibernate on a data layer), we can replace attributes with a Contract Resolver. Our POCO library does not have to reference anything

We just have to do extend the service layer:

public class MyResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(
         MemberInfo member,
         MemberSerialization memberSerialization)
    {

        var jProperty = base.CreateProperty(member, memberSerialization);

        var propertyInfo = member as PropertyInfo;
        if (propertyInfo == null)
        {
            return jProperty;
        }

        // just adjust in case if Property name is DefaultValue
        var isDefaultValueProeprty =
                  propertyInfo.Name.Equals("DefaultValue");

        if(isDefaultValueProeprty)
        {
            jProperty.PropertyName = "default";
        }

        return jProperty;
    }
    ...

This way we've provided the same information to serailizer as with the [JsonPropertyAttribute].

Now, we just have to use it. There are many ways (e.g. global) but we can do it for a controller only:

protected override void Initialize(HttpControllerContext context)
{
  base.Initialize(context);

  var jSettings = context.Configuration.Formatters.JsonFormatter.SerializerSettings;
  jSettings.ContractResolver = MyResolver;
}

Solution 3:

The class DropDownValues using camel convention:

class DropDownValues {
    public string[] values { get; set; }
    public string default { get; set; }
}

You can use prefix @ to passby but it is still not following C# coding convention.

The better solution which you can both avoid reserved keyword and still use C# coding convention is using CamelCasePropertyNamesContractResolver:

class DropDownValues {
    public string[] Values { get; set; }
    public string Default { get; set; }
}

And customize JsonFormatter to avoid convention mismatch between C# and json object as below:

var jsonFormatter = configuration.Formatters.JsonFormatter;
jsonFormatter.SerializerSettings = new JsonSerializerSettings()
{  
    ContractResolver = new CamelCasePropertyNamesContractResolver()
};