C#
serialization
dictionary
programming
software development

Serialize Class containing Dictionary member

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

Serializing a C# class with a dictionary property is common in APIs, caching layers, and configuration storage. Most cases work out of the box, but edge cases appear when key types are not strings or when backward compatibility is required. A robust approach combines clear model design, explicit serializer options, and predictable versioning rules.

Basic Serialization with System.Text.Json

For dictionaries with string keys, System.Text.Json usually handles serialization and deserialization automatically.

csharp
1using System;
2using System.Collections.Generic;
3using System.Text.Json;
4
5public class UserProfile
6{
7    public string UserId { get; set; } = string.Empty;
8    public Dictionary<string, string> Preferences { get; set; } = new();
9}
10
11public static class Program
12{
13    public static void Main()
14    {
15        var profile = new UserProfile
16        {
17            UserId = "u-100",
18            Preferences = new Dictionary<string, string>
19            {
20                ["theme"] = "dark",
21                ["language"] = "en"
22            }
23        };
24
25        string json = JsonSerializer.Serialize(profile, new JsonSerializerOptions
26        {
27            WriteIndented = true
28        });
29
30        Console.WriteLine(json);
31
32        var copy = JsonSerializer.Deserialize<UserProfile>(json);
33        Console.WriteLine(copy?.Preferences["theme"]);
34    }
35}

This is the best starting point for most modern .NET applications.

Non-String Dictionary Keys

JSON object property names are strings. When your dictionary key type is an enum, integer, or custom type, conversion behavior matters. For many primitive keys, serialization works through string conversion, but custom key types may need explicit converters.

csharp
1using System;
2using System.Collections.Generic;
3using System.Text.Json;
4using System.Text.Json.Serialization;
5
6public enum MetricType
7{
8    Requests,
9    Errors
10}
11
12public class MetricsEnvelope
13{
14    public Dictionary<MetricType, int> Metrics { get; set; } = new();
15}
16
17public static class Program
18{
19    public static void Main()
20    {
21        var data = new MetricsEnvelope
22        {
23            Metrics = new Dictionary<MetricType, int>
24            {
25                [MetricType.Requests] = 1200,
26                [MetricType.Errors] = 11
27            }
28        };
29
30        var options = new JsonSerializerOptions
31        {
32            Converters = { new JsonStringEnumConverter() },
33            WriteIndented = true
34        };
35
36        string json = JsonSerializer.Serialize(data, options);
37        Console.WriteLine(json);
38
39        var roundTrip = JsonSerializer.Deserialize<MetricsEnvelope>(json, options);
40        Console.WriteLine(roundTrip?.Metrics[MetricType.Errors]);
41    }
42}

Explicit converters keep wire format predictable and easier to maintain.

Designing for Compatibility

When serialized data is persisted long-term, schema evolution becomes important. Dictionary keys and values can change over time, so plan for missing entries and optional values.

Practical rules:

  • Initialize dictionary properties to empty instances.
  • Avoid null dictionary references in domain models.
  • Keep stable key naming conventions.
  • Add migration code when key names change.

You can also isolate transport models from domain models. Convert between them so storage format can evolve without breaking internal logic.

Newtonsoft.Json Interoperability

Some teams still use Newtonsoft.Json, especially in legacy codebases. It handles dictionary serialization well and includes rich converter support. If you mix serializers in one solution, verify that both produce compatible JSON for shared payloads.

For greenfield .NET applications, System.Text.Json is usually preferred for performance and built-in framework integration. For specialized polymorphic cases, evaluate feature needs before standardizing.

If dictionary content comes from untrusted sources, add value validation after deserialization. Large payloads, unexpected key patterns, or unsupported numeric formats can still pass parsing but fail business rules. A dedicated validation layer keeps serialization code simple and prevents bad state from entering your core domain model.

Testing Strategy

Add round-trip tests for representative samples, including:

  • Empty dictionary.
  • Missing dictionary field.
  • Unexpected keys.
  • Large dictionary size.

Round-trip assertions catch subtle converter issues early and prevent production regressions when serializer options change.

Common Pitfalls

A common pitfall is assuming custom object keys in dictionaries serialize naturally to stable JSON property names. Without explicit conversion rules, outputs may be fragile or unreadable.

Another issue is leaving dictionary properties nullable and then forgetting null checks after deserialization. Prefer non-null defaults to simplify business logic.

Developers also change key names in code without migration support for persisted payloads. This can silently lose access to stored values. Keep key evolution deliberate and version-aware.

Finally, using different serializers across services without compatibility tests can create subtle parsing mismatches. Validate payloads end to end when crossing service boundaries.

Summary

  • String-key dictionaries serialize easily with System.Text.Json.
  • Non-string keys often need explicit converter strategy.
  • Initialize dictionary properties to avoid null handling bugs.
  • Plan schema evolution for persisted dictionary payloads.
  • Add round-trip tests to protect serialization behavior over time.

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.