2

I'm trying to deserialize this json:

{
  "teaser": [{
              "id": "...",
              "type": "category",
              "url": "https:...",
            },{
              "id": "...",
              "type": "brand",
              "url": "https:...",
              "videoCount": 1,
            },{
              "id": "...",
              "type": "video",
              "url": "https:...",
              "headline": "...",
            }]
}

It has a list of teasers whereby each teaser is different depending on its type. These would be my objects:

public class StartPage
{
        public IList<Teaser> Teaser { get; set; }
}

public abstract class Teaser
{
        public string Id { get; set; }
        public string Url { get; set; }
}

public class Video : Teaser
{
        public string Headline { get; set; }
}

public class Brand : Teaser
{
        public int VideoCount { get; set; }
}

I am new to Json.NET and Xamarin and couldn't find a solution for this case yet. Before, when I was using Android Studio and Gson, I could register sybtypes the following way:

RuntimeTypeAdapterFactory<Teaser> teaserRuntimeTypeAdapterFactory = RuntimeTypeAdapterFactory.of(
                Teaser.class, "type")
                .registerSubtype(Video.class, Teaser.TYPE_VIDEO)
                .registerSubtype(Brand.class, Teaser.TYPE_BRAND)
                .registerSubtype(Category.class, Teaser.TYPE_CATEGORY);

        return new GsonBuilder()
                .registerTypeAdapterFactory(teaserRuntimeTypeAdapterFactory);

Is there a similar way to achieve what I want with Json.NET I have overlooked yet?

Curiousdev
  • 5,668
  • 3
  • 24
  • 38
  • have you read the documentation of json.net https://www.newtonsoft.com/json/help/html/Introduction.htm or samples https://www.newtonsoft.com/json/help/html/Samples.htm ? – makitocode Jan 23 '18 at 17:23
  • Read this stackoverflow thread about this topic: https://stackoverflow.com/questions/29528648/json-net-serialization-of-type-with-polymorphic-child-object – Almir Vuk Jan 23 '18 at 17:39

1 Answers1

0

What you can do is create custom JsonConverter please below snippet and find this dot net fiddle

string json ="{ 'Teaser': [{ 'id': '...', 'type': 'category', 'url': 'https:...', },{ 'id': '...', 'type': 'brand', 'url': 'https:...', 'videoCount': 1, },{ 'id': '...', 'type': 'video', 'url': 'https:...', 'headline': '...', }]}";

var list = JsonConvert.DeserializeObject<StartPage>(json);

public class BaseSpecifiedConcreteClassConverter : DefaultContractResolver
    {
        protected override JsonConverter ResolveContractConverter(Type objectType)
        {
            if (typeof(Teaser).IsAssignableFrom(objectType) && !objectType.IsAbstract)
                return null; // pretend TableSortRuleConvert is not specified (thus avoiding a stack overflow)
            return base.ResolveContractConverter(objectType);
        }
    }

public class BaseConverter : JsonConverter
    {
        static JsonSerializerSettings SpecifiedSubclassConversion = new JsonSerializerSettings() { ContractResolver = new BaseSpecifiedConcreteClassConverter() };

        public override bool CanConvert(Type objectType)
        {
            return (objectType == typeof(Teaser));
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            JObject jo = JObject.Load(reader);
            switch (jo["type"].Value<string>())
            {
                case "video":
                    return JsonConvert.DeserializeObject<Video>(jo.ToString(), SpecifiedSubclassConversion);
                case "brand":
                    return JsonConvert.DeserializeObject<Brand>(jo.ToString(), SpecifiedSubclassConversion);
                default:
                    throw new Exception();
            }
            throw new NotImplementedException();
        }

        public override bool CanWrite
        {
            get { return false; }
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            throw new NotImplementedException(); // won't be called because CanWrite returns false
        }
    }

Feel free to create custom logic in switch case hard code class name might not good idea instead create enum or something like but this is how you can achieve this type of scenario

Curiousdev
  • 5,668
  • 3
  • 24
  • 38
  • 1
    Thank you so much! That was exactly what I was looking for! Had to use System.Reflection and rewrite the ResolveContractConverter a bit to `typeof(Teaser).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo()) && !objectType.GetTypeInfo().IsAbstract` but everything else worked like a charm! – Tatiana V. R. Jan 24 '18 at 12:30