Corbie
Corbie

Reputation: 1066

Json.NET: deserialize any kind of object

I have a json file that looks like this:

{
  "tags": {
    "t1": {
      "description": "bar"
    },
    "t2": {
      "description": {
        "$ref": "./t2.md"
      }
    }
  }
}

and I would like to deserialize it with Json.NET like this:

var baz = JsonConvert.DeserializeObject<Baz>(File.ReadAllText(@"baz.json"));

//...

internal class Baz
{
    [JsonProperty("tags")]
    internal Tags Tags;
}

internal class Tags: Dictionary<string, Tag>
{
}

internal class Tag
{
    [JsonProperty("description")]
    internal Description Description;
}

internal class Description // FIXME: can be string, Dictionary or List
{
}

How can I define the Description class, that can be either a string or a Dictionary<string, string>? I have tried inheriting an abstract method, but the deserializer always returned null.

Upvotes: 1

Views: 252

Answers (1)

vernou
vernou

Reputation: 7590

You can create a custom deserializer : https://www.newtonsoft.com/json/help/html/CustomJsonConverter.htm

public class DescriptionConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return false;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.String)
        {
            //If is string, return the string
            return serializer.Deserialize(reader, objectType);
        }
        else
        {
            //If not string, try get the field '$ref'
            var obj = JObject.Load(reader);
            if (obj["$ref"] != null)
                return obj["$ref"].ToString();
            else
                throw new InvalidOperationException("Invalid Json");
        }
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

Then you can specify this converter in your model :

internal class Baz
{
    [JsonProperty("tags")]
    internal Tags Tags;
}

internal class Tags : Dictionary<string, Tag>
{
}

internal class Tag
{
    [JsonProperty("description")]
    [JsonConverter(typeof(DescriptionConverter))]
    internal string Description;
}

Finally, you can deserialize the json :

static void Main(string[] args)
{
    string json = @"{
        'tags': {
            't1': {
                'description': 'bar'
            },
            't2': {
                'description': {
                    '$ref': './t2.md'
                }
            }
        }
    }";
    var baz = JsonConvert.DeserializeObject<Baz>(json);
    Console.WriteLine("t1 : " + baz.Tags["t1"].Description);
    Console.WriteLine("t2 : " + baz.Tags["t2"].Description);
}

Output :

t1 : bar
t2 : ./t2.md

Upvotes: 2

Related Questions