1

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.

5
  • Description's field '$ref' can have other name? Commented Aug 26, 2020 at 15:06
  • No, its either a string or a {"$ref": "..."} object. Commented Aug 26, 2020 at 15:26
  • You need a custom deserializer : newtonsoft.com/json/help/html/CustomJsonConverter.htm. Example : stackoverflow.com/questions/40439290/… Commented Aug 26, 2020 at 15:37
  • 1
    @RoarS. it is a JS object with one Key-Value-Pair, so equal to a dictionary with one entry. Commented Aug 26, 2020 at 16:17
  • 1
    @Vernou Thank you, this is working with obj["$ref"] in the second link you have posted. Please add an answer, so that I can accept it. Commented Aug 26, 2020 at 16:19

1 Answer 1

2

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
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.