20250307 ‐ deserialization with generics - cywongg/2025 GitHub Wiki

Below is a conceptual example illustrating how you can **strongly type** your deserialization using generics when you already know exactly which subclass you expect. Afterwards, I’ll explain the syntax and meaning of `where TMarketData : AbstractMarketData`.

## Example

Suppose you have the following abstract base class and subclasses:

```csharp
public abstract class AbstractMarketData
{
    public DateTime DateRef { get; set; }
}

public class Repo : AbstractMarketData
{
    public string Iosin { get; set; }
    // Other Repo-specific properties...
}

public class Dividend : AbstractMarketData
{
    public double SpotRef { get; set; }
    // Other Dividend-specific properties...
}

public class Volatility : AbstractMarketData
{
    public List<double> Strikes { get; set; }
    // Other Volatility-specific properties...
}
```

### “RootObject” with Generics

You might receive JSON whose root structure looks something like:

```json
{
  "MarketData": {
    "Iosin": "XYZ123",
    "DateRef": "2025-01-01T00:00:00"
  }
}
```

…for a Repo. Or something like:

```json
{
  "MarketData": {
    "Strikes": [100, 110, 120],
    "DateRef": "2025-01-01T00:00:00"
  }
}
```

…for a Volatility.

To deserialize cleanly, you can define a generic `RootObject<TMarketData>`:

```csharp
public class RootObject<TMarketData> where TMarketData : AbstractMarketData
{
    public TMarketData MarketData { get; set; }
}
```

**What does `where TMarketData : AbstractMarketData` mean?**  
- This **generic constraint** ensures that `TMarketData` is always a class that **inherits** from `AbstractMarketData`.  
- That means you can’t do something like `RootObject<int>`, because `int` does not derive from `AbstractMarketData`.  
- You can only do `RootObject<Repo>`, `RootObject<Dividend>`, `RootObject<Volatility>`, or any other class that extends `AbstractMarketData`.

### Deserializing for a Known Type

Now, let’s say in your application logic, you have an enum that indicates the kind of market data you want to load:

```csharp
public enum DataType
{
    Repos,
    Dividends,
    Volatilities
}
```

In your “getMarketData” method, you already know which `DataType` you want to load, so you can choose the appropriate *concrete* class:

```csharp
public AbstractMarketData GetMarketData(DataType dataType)
{
    // Call API, read JSON string
    var response = client.GetAsync(uriBuilder.Uri).Result;
    var json = response.Content.ReadAsStringAsync().Result;

    switch (dataType)
    {
        case DataType.Repos:
            // Deserialize expecting a Repo
            var repoRoot = JsonConvert.DeserializeObject<RootObject<Repo>>(json);
            return repoRoot.MarketData;

        case DataType.Dividends:
            // Deserialize expecting a Dividend
            var dividendRoot = JsonConvert.DeserializeObject<RootObject<Dividend>>(json);
            return dividendRoot.MarketData;

        case DataType.Volatilities:
            // Deserialize expecting a Volatility
            var volRoot = JsonConvert.DeserializeObject<RootObject<Volatility>>(json);
            return volRoot.MarketData;

        default:
            throw new InvalidOperationException($"Unrecognized DataType: {dataType}");
    }
}
```

With this pattern:

1. **Strong Typing** – Each case is deserialized into the proper subclass.  
2. **Maintainability** – When you add a new subclass (e.g., `FXSwap` or something else), just add a new enum entry **and** add a new `case` block.  
3. **Elimination of Casting** – No need to cast `(Repo)data` or `(Volatility)data` in the code. The generic deserialization handles that for you.

---

## Why Use `RootObject<TMarketData>`?

Often, the JSON you get has a wrapper object like:

```json
{
    "SomeHeader": { "SomeInfo": "..." },
    "MarketData": { ... your actual data ... }
}
```

Having a **generic root** type is advantageous because:

- You don’t have to repeatedly define different root classes for each subtype.  
- You keep the “outer” structure (`RootObject<T>`) consistent while the **inner** type of `MarketData` can vary.

If your JSON is simpler and **only** contains the object for the subclass itself, you might not need the `RootObject<TMarketData>` at all, and could do something like:

```csharp
var repo = JsonConvert.DeserializeObject<Repo>(json);
```

But very often the JSON is wrapped with extra metadata, so a `RootObject<T>` is a clean approach.

---

## Summary

- `public class RootObject<TMarketData> where TMarketData : AbstractMarketData`
  - This means: “Define a class `RootObject<TMarketData>`, and for the type parameter `TMarketData`, it **must** derive from `AbstractMarketData`.”
  - The `where` clause is called a **generic type constraint**.  
- You then sanitize each **specific** scenario (Repos, Dividends, Volatilities) by using 
  ```csharp
  JsonConvert.DeserializeObject<RootObject<Repo>>(json)
  ```
  and so on.  
- This ensures that the final type of the `.MarketData` property lines up exactly with your domain model (e.g., `Repo` vs. `Dividend` vs. `Volatility`).  

This is a good approach if, in your higher-level logic, you truly **know** which type of market data you are fetching from each call.
⚠️ **GitHub.com Fallback** ⚠️