1

I'm using WebAPI, which relies on JSON .NET for the JSON format. On the C# side I have a DTO that looks like this:

public class DTO1 : IManifestContainer
{
    public string FirstName { get; set; }
    public string LastName { get; set; }        
    public HashSet<string> Manifest { get; private set; }
}
public interface IManifestContainer
{
    HashSet<string> Manifest { get; }
}

The idea of the IManifestContainer interface is to keep track of the properties that the client is actually sending to the server in the JSON object. For example, if the client sends this JSON:

{"FirstName":"Jojo"}

The Manifest hashset will contain the "FirstName" only. If the client sends:

{"FirstName":"Jojo", "LastName":"Jones"}

The Manifest hashset will contain both "FirstName" and "LastName".

I tried implementing a JsonConverter like this:

public class ManifestJsonConverter : JsonConverter
{
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
    if (reader.TokenType == JsonToken.Null)
    {
        return null;
    }

    JObject jObject = JObject.Load(reader);

    // Convert the JObject to a C# object?? 
    // Passing the serializer will call this method again
    object retVal = jObject.ToObject(objectType, serializer);

    IManifestContainer manifestContainer = (IManifestContainer) retVal;

    foreach (var jProperty in jObject.Properties())
    {
        manifestContainer.Manifest.Add(jProperty.Name);
    }
    return retVal;
}

public override bool CanConvert(Type objectType)
{
    return typeof (IManifestContainer).IsAssignableFrom(objectType);
}
}

I need to load the JObject to obtain all the properties coming from the client, but then I don't know how to create the instance of "objectType" (the C# DTO) from the JObject.

oscarmorasu
  • 901
  • 3
  • 11
  • 28

2 Answers2

1

After reading this other post I came up with this implementation. This covers all cases.

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
        {
            return null;
        }

        JObject jObject = JObject.Load(reader);

        JArray manifestArray = new JArray();

        foreach (var jProperty in jObject.Properties())
        {
            manifestArray.Add(jProperty.Name);
        }

        jObject["Manifest"] = manifestArray;

        var retVal = Activator.CreateInstance(objectType);

        serializer.Populate(jObject.CreateReader(), retVal);

        return retVal;
    }
Community
  • 1
  • 1
oscarmorasu
  • 901
  • 3
  • 11
  • 28
0

You can try using the overload of JObject.ToObject that does not take a JsonSerializer. In that case the ToObject method will use a new serializer instance that does not know about the converter, which should allow it to work, as long as you have not decorated your DTO classes with [JsonConverter] attributes.

object retVal = jObject.ToObject(objectType);

If that doesn't work, your other option is to create the DTO instances manually and populate their properties via reflection.

Brian Rogers
  • 125,747
  • 31
  • 299
  • 300
  • I need to keep the same serializer because it contains other settings that I have to preserve while deserializing the whole graph. – oscarmorasu Mar 25 '14 at 23:48