.NET
Key/Value Pair
Serialization
Generics
Programming

Is there a serializable generic Key/Value pair class in .NET?

Object-Oriented Design practice on Codemia

Turn requirements into classes, and defend the design, on the problems that come up in OOD rounds.

Practice OOD

Introduction

KeyValuePair<TKey, TValue> is the built-in generic key/value struct in .NET, and it is serializable with most modern serializers (System.Text.Json, Newtonsoft.Json, XmlSerializer). However, it is a read-only struct — its Key and Value properties have no setters, which causes issues with some older serializers that require settable properties. For those cases, you can create a simple custom class or use Tuple<T1, T2>, Dictionary<TKey, TValue>, or anonymous types.

KeyValuePair with System.Text.Json

csharp
1using System.Text.Json;
2
3var kvp = new KeyValuePair<string, int>("age", 30);
4
5string json = JsonSerializer.Serialize(kvp);
6Console.WriteLine(json);
7// {"Key":"age","Value":30}
8
9var deserialized = JsonSerializer.Deserialize<KeyValuePair<string, int>>(json);
10Console.WriteLine($"{deserialized.Key}: {deserialized.Value}");
11// age: 30

System.Text.Json handles KeyValuePair natively since .NET Core 3.0.

KeyValuePair with Newtonsoft.Json

csharp
1using Newtonsoft.Json;
2
3var kvp = new KeyValuePair<string, int>("score", 95);
4
5string json = JsonConvert.SerializeObject(kvp);
6Console.WriteLine(json);
7// {"Key":"score","Value":95}
8
9var deserialized = JsonConvert.DeserializeObject<KeyValuePair<string, int>>(json);
10Console.WriteLine($"{deserialized.Key}: {deserialized.Value}");
11// score: 95

Newtonsoft.Json serializes KeyValuePair by default.

KeyValuePair with XmlSerializer

XmlSerializer requires settable properties. KeyValuePair<TKey, TValue> has read-only properties, so direct XML serialization fails:

csharp
1using System.Xml.Serialization;
2
3var kvp = new KeyValuePair<string, int>("name", 42);
4var serializer = new XmlSerializer(typeof(KeyValuePair<string, int>));
5// This may throw or produce incomplete XML because Key and Value have no setters

Custom Serializable Key/Value Class

Create a class with settable properties for XML or legacy serializer compatibility:

csharp
1[Serializable]
2public class SerializableKeyValue<TKey, TValue>
3{
4    public TKey Key { get; set; }
5    public TValue Value { get; set; }
6
7    public SerializableKeyValue() { }  // Parameterless constructor required for serialization
8
9    public SerializableKeyValue(TKey key, TValue value)
10    {
11        Key = key;
12        Value = value;
13    }
14
15    // Convert to/from KeyValuePair
16    public KeyValuePair<TKey, TValue> ToKeyValuePair() =>
17        new(Key, Value);
18
19    public static SerializableKeyValue<TKey, TValue> FromKeyValuePair(KeyValuePair<TKey, TValue> kvp) =>
20        new(kvp.Key, kvp.Value);
21}
22
23// Usage
24var item = new SerializableKeyValue<string, int>("score", 95);
25
26// XML serialization works
27var xmlSerializer = new XmlSerializer(typeof(SerializableKeyValue<string, int>));
28using var writer = new StringWriter();
29xmlSerializer.Serialize(writer, item);
30Console.WriteLine(writer.ToString());

Serializing Dictionary Entries

A Dictionary<TKey, TValue> serializes as a JSON object, which is often more natural:

csharp
1using System.Text.Json;
2
3var dict = new Dictionary<string, int>
4{
5    ["alice"] = 95,
6    ["bob"] = 87,
7    ["charlie"] = 92
8};
9
10string json = JsonSerializer.Serialize(dict);
11Console.WriteLine(json);
12// {"alice":95,"bob":87,"charlie":92}
13
14var deserialized = JsonSerializer.Deserialize<Dictionary<string, int>>(json);

For a list of key/value pairs (allowing duplicate keys):

csharp
1var pairs = new List<KeyValuePair<string, int>>
2{
3    new("math", 95),
4    new("math", 87),  // Duplicate key OK in a list
5    new("science", 92)
6};
7
8string json = JsonSerializer.Serialize(pairs);
9// [{"Key":"math","Value":95},{"Key":"math","Value":87},{"Key":"science","Value":92}]

Tuple as an Alternative

csharp
1// ValueTuple (C# 7+)
2var item = (Key: "name", Value: "Alice");
3string json = JsonSerializer.Serialize(item);
4// {"Key":"name","Value":"Alice"}  — named tuples serialize with their names
5
6// Tuple<T1, T2>
7var tuple = Tuple.Create("age", 30);
8json = JsonSerializer.Serialize(tuple);
9// {"Item1":"age","Item2":30}

Tuples work for serialization but use Item1/Item2 naming (for Tuple<>) unless you use named ValueTuples.

Record Type (C# 9+)

Records provide built-in equality, immutability, and serialization support:

csharp
1public record KeyValue<TKey, TValue>(TKey Key, TValue Value);
2
3var item = new KeyValue<string, int>("score", 95);
4string json = JsonSerializer.Serialize(item);
5// {"Key":"score","Value":95}
6
7var deserialized = JsonSerializer.Deserialize<KeyValue<string, int>>(json);
8Console.WriteLine(deserialized);
9// KeyValue { Key = score, Value = 95 }

Records are the modern, clean approach for immutable key/value types.

DataContract Serialization

For WCF or DataContract-based serialization:

csharp
1using System.Runtime.Serialization;
2
3[DataContract]
4public class SerializableKVP<TKey, TValue>
5{
6    [DataMember]
7    public TKey Key { get; set; }
8
9    [DataMember]
10    public TValue Value { get; set; }
11}

Common Pitfalls

  • XmlSerializer requiring settable properties: KeyValuePair<TKey, TValue> has read-only Key and Value properties. XmlSerializer needs setters for deserialization. Create a custom class with settable properties or use a different serializer.
  • Parameterless constructor required: Many serializers (XmlSerializer, some JSON deserializers) require a parameterless constructor. KeyValuePair<TKey, TValue> has one (it is a struct), but custom classes must explicitly define one.
  • Dictionary keys must be strings for JSON object serialization: System.Text.Json serializes Dictionary<string, T> as a JSON object. Non-string keys (e.g., Dictionary<int, string>) are serialized differently or may fail. Use JsonConverter for custom key types.
  • Duplicate keys in Dictionary: Dictionary<TKey, TValue> does not allow duplicate keys. If you need duplicate keys, use List<KeyValuePair<TKey, TValue>> instead.
  • ValueTuple field names lost at runtime: Named ValueTuple fields ((string Name, int Age)) are erased by the compiler. Serializers may produce Item1/Item2 instead of Name/Age. Use records or classes for reliable named serialization.

Summary

  • KeyValuePair<TKey, TValue> works with System.Text.Json and Newtonsoft.Json out of the box
  • For XmlSerializer, create a custom class with settable properties and a parameterless constructor
  • Use Dictionary<string, TValue> when keys are unique and string-typed for natural JSON serialization
  • Use List<KeyValuePair<TKey, TValue>> when duplicate keys are needed
  • C# records (record KeyValue<TKey, TValue>(TKey Key, TValue Value)) are the cleanest modern approach
  • Always test serialization round-trips (serialize then deserialize) to verify data integrity

Related reading
Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Track what you have practised

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

Object-Oriented Design practice on Codemia

Turn requirements into classes, and defend the design, on the problems that come up in OOD rounds.

Practice OOD

All Rights Reserved.