C# – suggest structure for classes that maps to json with dynamic data without using dynamic or object reference

cjson

this is a kind of data i have to de-serialize

{
    "id": "M:11427",
    "title": "DAX30",
    "nextStartId": "S:727831",
    "sections": [
        {
            "type": "HIGHLIGHTS",
            "baseResults": [ values of type highlights ],
            "hasMore": true
        },
        {
            "type": "CHART",
            "hasMore": false,
            "chartTypes": [ string values ]
        },
        {
            "type": "TWEETS",
            "baseResults": [ values of type tweets ],
            "hasMore": true
        }]
}

I have to serialize & deserialize them all. I want to create something that can hold the values corresponding to baseResults.

there is a main class that represent the whole json

class Data 
{
...
ObservableCollection<Section> sections{get;set;}
...
}

then there is a class that represents the data in sections array of main json

class Section
{
string type{get;set;}// thi elements decides what kind of data would be in baseResults
dynamic baseResults{get;set;} //it should hold ObservableCollection of either Highlights or Tweets etc.
}

base class for type of data coming in baseResults array is an abstract class class CategoryData
and its children are class Highlights & class Tweets

I am using dynamic since I can not assign an ObservableCollection<CategoryData> with ObservableCollection<Highlights>. But I don't want to use this dynamic or object type instead I want something relevant. please suggest me what could be a better approach for this problem.

Best Answer

If I'm understanding you correctly, you're trying to find a statically-typed way to say "a value of type Section can be either a value of type Highlights, Chart, or Tweets". There are many terms for this, including tagged union, discriminated union, variant, or sum type but they all refer to the same concept. Sum types are a standard feature in statically-typed functional programming languages, but with a bit of ingenuity you can roll your own in C#. Using that answer as a template, and adding sealed to make sure no one adds any extra subclasses, your Section type would look something like this:

public abstract class Section
{
    // Prevent subclassing outside of this scope
    private Section() {}

    // Subclass implementation calls the appropriate function.
    public abstract R Match<R>(Func<H, R> IfHighlight, Func<C, R> IfChart, Func<T, R> IfTweet);

    // Convenience wrapper for when the caller doesn't want to return a value
    // from the match expression.
    public sealed void Match(Action<H> IfHighlight, Action<C> IfChart, Action<T> IfTweet)
    {
        this.Match<int>(
            IfHighlight: Section => { IfHighlight(Section); return 0; },
            IfChart: Section => { IfChart(Section); return 0; },
            IfTweet: Section => { IfTweet(Section); return 0; }
        );
    }

    // From here on down, you define your specific types as usual, and override Match to call
    // the correct function

    public sealed class Highlight : Section
    {
        // Implementation goes here

        public override R Match<R>(Func<H, R> IfHighlight, Func<C, R> IfChart, Func<T, R> IfTweet)
        {
            return IfHighlight(this);
        }
    }

    public class Chart : Section
    {
        // Implementation goes here

        public override R Match<R>(Func<H, R> IfHighlight, Func<C, R> IfChart, Func<T, R> IfTweet)
        {
            return IfChart(this);
        }
    }

    public class Tweet : Section
    {
        // Implementation goes here

        public override R Match<R>(Func<H, R> IfHighlight, Func<C, R> IfChart, Func<T, R> IfTweet)
        {
            return IfTweet(this);
        }
    }
}

With that in place, you can declare variables of type Section, and act on them by passing 3 functions to the Match methods:

Section section = deserializeJson()
section.Match(
    IfHighlight: highlight => /* code to use if you get a Highlight */
    IfChart: chart => /* ... */
    IfTweet: tweet => /* ... */
);

You could just as easily have a collection of Sections and operate on them that way when you iterate over it.