Gabriel S.
Gabriel S.

Reputation: 1347

Custom runtime serialization of members

I was wondering if anyone could point me in the right direction for solving easily the following problem:

Suppose I have a Player class in my .NET code, which looks like this:

public class Player
{
   public int Id { get; set; }
   public string Name { get; set; }
   public long Score { get; set; }
}

I need to serialize this class into a JSON string (using JSON.NET) and use it when POST-ing to a web service. However, the thing is that some of the service's endpoints explicitly prohibit in the JSON string the occurrence of certain members. For instance, a "post score" endpoint would allow all the 3 members to be included in the string, while a "register player" endpoint would only allow Id and Name to be present (otherwise, a bad request is thrown back to the client). Now I know that I could make 2 different classes (e.g. Player and CompetitivePlayer), each containing the required (sub)set of members, however for practical purposes let's suppose I can't do this or want to avoid this (my actual data objects are more complex than the Player class given here simply as an example).

So what I actually want is to tell the JSON serializer at runtime that only certain members of an object must be serialized in situation X, while in situation Y a whole different subset is to be serialized. At first I thought that implementing my own ContractResolver would help, but as it turns out this is only called once per object's type, not per object itself when serializing it. Now the only solution I can think of is to subclass JSONSerializer and have it use a JSONWriter that ignores the properties whose names are included in a list of strings given as argument, perhaps - although I'm not quite sure if this plan can work. Is there a simpler solution for what I'm trying to achieve?

Upvotes: 1

Views: 957

Answers (2)

Gabriel S.
Gabriel S.

Reputation: 1347

Ok, after looking over the JSON.NET source code I found the exact spot that prevented me from custom serializing those properties: the CamelCasePropertyNamesContractResolver class.

Before writing the original question, I tried to implement custom serialization as described here, at the IContractResolver section. However, instead of inheriting directly from DefaultContractResolver I used CamelCasePropertyNamesContractResolver (I needed camel case here) which, after looking inside its code, sets a "share cache" flag that prevented some of its methods from being called, for performance reasons (which I'm willing to sacrifice in this scenario). Thus, CreateProperties() was called only once per object type, instead of every time my object needed to be serialized. So now my contract resolver class looks like this:

class OptionalPropertiesContractResolver : DefaultContractResolver
{
    //only the properties whose names are included in this list will be serialized
    IEnumerable<string> _includedProperties;

    public OptionalPropertiesContractResolver(IEnumerable<string> includedProperties)
    {
        _includedProperties = includedProperties;
    }

    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        return (from prop in base.CreateProperties(type, memberSerialization)
                where _includedProperties.Contains(prop.PropertyName)
                select prop).ToList();
    }

    protected override string ResolvePropertyName(string propertyName)
    {
        // lower case the first letter of the passed in name
        return ToCamelCase(propertyName);
    }

    static string ToCamelCase(string s)
    {
        //camel case implementation
    }
}

Just wanted to let others know of this particular situation in case they ever come across it.

Upvotes: 3

b0rg
b0rg

Reputation: 1897

I would create contract classes and use AutoMapper to map to them from Player class and serialize them as needed. I.e. 'PlayerContract', 'CompetitivePlayerContract', etc.

It doesn't really matter that those classes only represent contracts to your service.

Upvotes: 0

Related Questions