Introduction
Unity projects constantly need mappings: an item ID to its definition, a status-effect type to its parameters, an input name to a Sprite, or a cell coordinate to its gameplay data. In ordinary C#, Dictionary<TKey, TValue> is the natural choice.
Until now, however, Unity could not directly serialize a regular Dictionary into a Scene, Prefab, or ScriptableObject, and the default Inspector could not display one. Teams usually worked around that limitation by converting keys and values into two Lists through ISerializationCallbackReceiver, adopting an existing SerializableDictionary, or bringing in Odin Inspector and Odin Serializer.
Unity 6.6 finally adds built-in serialization for the standard Dictionary<TKey, TValue>. The Inspector gets a dedicated editor with entry addition and removal, sorting, duplicate-key detection, and adjustable column widths.
That does not mean every custom serializer or every use of Odin immediately becomes obsolete. This article looks at the supported range, Inspector behavior, Prefab pitfalls, and migration concerns so that you can decide where the new built-in support is enough—and where it is not.
Important: This article is based on the Unity 6.6 Beta documentation available on July 15, 2026, specifically
6000.6.0b3. Attribute names, behavior, and limitations may change before the final release. Before adopting the feature in production, check the release notes and manual for the exact Unity version you intend to ship.
The conclusion first
When all of the following are true, there is now far less reason to create a new SerializableDictionary implementation:
- Unity 6.6 or later can be your minimum supported Editor version.
- The key and value types fit Unity's supported serialization rules.
- The standard Inspector provides enough editing functionality.
- You can accept the current Prefab Override behavior.
In this article, a plain Dictionary means the exact, non-derived Dictionary<TKey, TValue> type whose key and value are both supported by Unity serialization.
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(menuName = "Example/Item Price Table")]
public sealed class ItemPriceTable : ScriptableObject
{
[SerializeField]
private Dictionary<string, int> prices = new();
public bool TryGetPrice(string itemId, out int price)
=> prices.TryGetValue(itemId, out price);
}
This example can be created from Create > Example > Item Price Table in the Project window. The prices field is stored in the ScriptableObject asset and can be edited in the Inspector.
The standard feature does not replace every existing solution:
| Requirement | Default direction |
|---|---|
| Support Unity 6.5 or earlier | Keep the existing custom implementation or Odin |
Serialize SortedDictionary, HashSet, or a Dictionary-derived type |
Use custom serialization |
| Store interfaces, abstract types, or polymorphic managed references | Consider Odin or another dedicated mechanism |
| Provide search, bulk editing, or advanced validation UI | A Custom Editor or Odin is still useful |
| Migrate a large amount of existing custom or Odin data | Plan an explicit migration process |
The most accurate interpretation is not “Unity solved every serialization problem.” It is that the extremely common need to serialize an ordinary Dictionary without a custom wrapper has finally become a built-in feature.
Why Dictionary serialization used to be painful
Through Unity 6.5, the official serialization rules did not support Dictionary. A common workaround copied entries into List<TKey> and List<TValue> before serialization, then paired elements with the same index to reconstruct the Dictionary after loading.
The basic idea is simple. Production behavior is not. A team still has to define:
- What happens when the key and value counts differ.
- How null and duplicate keys are handled.
- What work is safe inside serialization callbacks.
- How Undo and Prefab Overrides behave in a custom Inspector.
- Whether each concrete type needs a derived class or Property Drawer.
- How old serialized data remains compatible after the implementation changes.
A short serializer may be enough to “make it save.” Turning it into a tool that an entire team can edit safely is much more expensive. That is also why Odin has remained popular: it can solve both serialization and Inspector presentation instead of leaving every project to maintain both layers.
The important part of Unity 6.6 is therefore not merely that Dictionary data can be stored. Unity itself now owns the Inspector UI, warnings, Prefab Override integration, and serialization-rule diagnostics around the feature.
Minimal setup—and [SerializeField] is required even for public fields
The basic form is straightforward:
using System.Collections.Generic;
using UnityEngine;
public sealed class Inventory : MonoBehaviour
{
[SerializeField]
private Dictionary<string, int> itemCounts = new();
}
The same feature is available in ScriptableObject and inside nested classes or structs marked with [Serializable].
The easiest rule to miss is that Dictionary requires explicit opt-in. Unlike many traditionally supported Unity field types, making the field public is not enough.
// Demonstration only: public, but not serialized.
public Dictionary<string, int> notSerialized = new();
// Demonstration only: public and explicitly serialized.
[SerializeField]
public Dictionary<string, int> serialized = new();
notSerialized remains unsaved even though it is public. Any Dictionary that should be serialized needs [SerializeField], whether the field is private or public.
This avoids turning every public Dictionary in an existing project into serialized data merely because the Editor was upgraded. It makes serialization intentional rather than implicit.
In production code, private plus [SerializeField] is usually easier to control than a freely replaceable public field. Expose only the operations or read-only access that other code actually needs.
The serialization rules analyzer reports a public Dictionary without [SerializeField] as UAC1015. If a Dictionary is deliberately a runtime-only cache, consider marking that intent with [NonSerialized] rather than leaving its role ambiguous.
[SerializeReference] cannot be applied to the Dictionary field
Dictionary entries are stored inline as a sequence of key-value entries, so [SerializeReference] cannot be applied to the Dictionary field itself.
[SerializeReference] // Invalid
private Dictionary<string, IEffect> effects = new();
This example is unsupported for a second reason: the value type is an interface. Unity 6.6's Dictionary feature does not extend the polymorphic managed-reference behavior of [SerializeReference] into Dictionary fields.
The declared type must be exactly Dictionary<TKey, TValue>
The manual requires the field's declared type to be the exact Dictionary<TKey, TValue> type.
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public sealed class ItemPriceDictionary : Dictionary<string, int>
{
}
public sealed class LegacyItemDatabase : ScriptableObject
{
[SerializeField]
private ItemPriceDictionary prices = new(); // Not handled by the new built-in backend
}
If an existing project uses derived types such as StringIntDictionary, upgrading to Unity 6.6 does not silently convert them to the new built-in representation. You need to change the declared type and migrate the stored data explicitly.
What the built-in Inspector can and cannot do
The standard Dictionary editor supports at least the following operations:
- Add and remove entries with
+and-. - Resize the key and value columns.
- Sort the displayed rows by key in ascending or descending order.
- Warn about duplicate and null keys.
- Switch the display layout.
- Show Prefab Overrides at the key or value level.
That covers a great deal of ordinary project configuration, but several details matter in production.
Sorting changes the display, not the stored order
Clicking the key column changes only the order shown in the Inspector. The serialized entry order remains insertion order.
This distinction matters for YAML diffs and Prefab Overrides. A Dictionary may look alphabetically sorted in the Inspector while the serialized file still follows a different order. Game logic also should not assign meaning to the Dictionary's enumeration order.
When priority or presentation order is part of the specification, store it explicitly in the value or use a List as the authoritative structure.
Duplicate keys can remain temporarily in serialized data
The Inspector does not immediately delete a duplicate row while you are editing. It keeps the row and shows a warning so that you can correct the data without losing the value.
According to the Unity 6.6 Beta documentation, loading serialized data that still contains duplicates produces a warning, and only the first entry in serialized order is added to the runtime Dictionary. Verify that behavior again in the final release and in the exact patch version used by your project.
As a result, the number of rows visible in serialized data may differ from Dictionary.Count at runtime. Which value survives depends on serialized insertion order, not the current sort order in the Inspector.
Do not treat the warning as harmless editor noise. Duplicate-key checks should be part of your content-validation process.
A Custom Inspector can retrieve duplicate positions through SerializedProperty.GetDictionaryDuplicateEntryIndices.
Null keys are preserved as placeholders, then omitted at runtime
When a UnityEngine.Object reference is used as a key, an unassigned field or missing reference can create a null-key row. The Inspector preserves that row as a placeholder so that the value is not immediately lost during editing.
A runtime Dictionary still cannot contain a null key. Unity warns during loading and leaves that entry out of the reconstructed Dictionary.
In other words, “visible in the Inspector” does not necessarily mean “present in the runtime Dictionary.”
There is no Multi-Object Editing, search, or filtering
In the Unity 6.6 Beta, Dictionary fields do not support Multi-Object Editing. Selecting multiple GameObjects or assets displays a Help Box instead of the Dictionary editor.
The standard Inspector also has no entry search or filtering. That is perfectly reasonable for a few dozen entries. It becomes a workflow problem when designers or developers must edit hundreds or thousands of records directly.
At that scale, a Custom Editor or external authoring pipeline is still the better tool.
Supported key and value types
The following are representative supported types rather than an exhaustive list. Run the exact types used by your project through the serialization rules analyzer for the Unity version you adopt.
- Primitive and common built-in types such as
int,float,double,bool, andstring. - Enums whose underlying type is 32 bits or smaller.
- Unity built-in serializable types such as
Vector2,Vector3, andColor. - Custom classes and structs marked with
[Serializable]. - References to types derived from
UnityEngine.Object.
Ordinary interfaces, abstract types, and custom types without [Serializable] are not supported. UnityEngine.Object inheritance is different because those values are stored as Unity object references.
Lists and arrays can be values, but not keys
These fields are valid:
[SerializeField]
private Dictionary<int, List<string>> stageEnemies = new();
[SerializeField]
private Dictionary<string, int[]> scoreTables = new();
Collection-like IEnumerable types such as List and array cannot be used as keys. string is the exception.
// Unsupported
[SerializeField]
private Dictionary<List<int>, string> invalid = new();
A mutable collection is a dangerous key even outside Unity's restrictions. Changing its contents can change equality or hash behavior after it has already been inserted.
A Dictionary cannot be placed directly inside a List or array
The following forms are unsupported:
[SerializeField]
private List<Dictionary<string, int>> histories = new(); // Unsupported
[SerializeField]
private Dictionary<string, int>[] tables; // Unsupported
Wrap the Dictionary in a serializable class or struct instead:
[System.Serializable]
public sealed class ItemCountTable
{
[SerializeField]
private Dictionary<string, int> values = new();
}
[SerializeField]
private List<ItemCountTable> snapshots = new();
The official manual also shows a Dictionary used as the value of another Dictionary:
[SerializeField]
private Dictionary<int, Dictionary<string, SkillLevel>> skillsPerTier = new();
That combination can feel surprising: List<Dictionary<...>> is unsupported, while a nested Dictionary value is documented. Do not infer the nesting rules from intuition. Run the concrete type through the analyzer and verify it by saving and reloading the Scene or asset.
Custom key types still need a correct equality design
When a custom class or struct is used as a key, implement Equals and GetHashCode appropriately. Without an override, two class instances with identical serialized fields are still different keys because reference equality is used.
A struct can work with its default equality behavior, but Unity's documentation recommends implementing IEquatable<T> for value-type keys rather than relying on reflection-based default comparison.
using System;
[Serializable]
public struct GridCellKey : IEquatable<GridCellKey>
{
public int x;
public int y;
public bool Equals(GridCellKey other)
=> x == other.x && y == other.y;
public override bool Equals(object obj)
=> obj is GridCellKey other && Equals(other);
public override int GetHashCode()
{
unchecked
{
return (x * 397) ^ y;
}
}
}
For class keys in particular, do not modify fields that participate in equality after inserting the object into a Dictionary. If the hash code changes, the key can become effectively unreachable even though the entry still exists internally.
Struct keys are copied on insertion, but the clearest way to change a key is still to remove the old entry and add a new one. Keep mutable state in the value and treat keys as effectively immutable.
If a custom Equals or GetHashCode implementation throws while Unity reconstructs the Dictionary, Unity skips affected entries and logs a warning. Built-in serialization cannot protect a project from an incorrect equality implementation.
Serializable does not always mean Dictionary is the right structure
“Unity can now save it” and “Dictionary best represents the data” are separate questions.
The order itself has meaning
Dialogue sequence, attack priority, tutorial progression, and similar data belong naturally in a List. Dictionary exists for key-based lookup, and its Inspector sorting is display-only.
When order is part of the specification, keep the List as the source of truth and build a lookup Dictionary during initialization when necessary.
[SerializeField]
private List<ItemDefinition> items = new();
private readonly Dictionary<string, ItemDefinition> itemById = new();
Here, itemById is a derived cache. It should not be serialized; rebuild it in Awake or another initialization step. Not every Dictionary needs [SerializeField] simply because the option now exists.
Multiple rows with the same key are legitimate data
An enemy may have several drop candidates. Multiple events may occur at the same timestamp. In those cases, repeated keys are not invalid input—they are the model.
Do not add artificial sequence numbers merely to force uniqueness. Represent the relationship directly with something like Dictionary<EnemyId, List<DropEntry>> or with an ordinary List.
An external format is the source of truth
A server API, CSV file, spreadsheet, or existing save-data schema may own the real contract. The fact that Unity can edit a Dictionary in the Inspector is not a reason to replace that external schema with Unity's internal representation.
Keeping external DTOs separate from Unity authoring data, then converting at a boundary, often makes compatibility much easier to manage.
Humans must edit a very large dataset
The standard Inspector has no search or filtering. For large datasets, input validation, diff review, referential integrity, and bulk updates matter more than whether the final generated object happens to be a serialized Dictionary.
The built-in Dictionary may be a useful storage target. It is not a replacement for the complete data-authoring workflow.
Customizing the Inspector with DictionaryDisplay
[DictionaryDisplay] lets you define initial column labels, column width, and layout.
using System;
using System.Collections.Generic;
using UnityEngine;
[Serializable]
public sealed class ItemDefinition
{
public string displayName;
public int price;
public Sprite icon;
}
[CreateAssetMenu(menuName = "Example/Item Database")]
public sealed class ItemDatabase : ScriptableObject
{
[SerializeField]
[DictionaryDisplay(
keyLabel = "Item ID",
valueLabel = "Definition",
keyColumnFraction = 0.35f,
layout = DictionaryLayout.OneColumnWithValueFoldout)]
private Dictionary<string, ItemDefinition> items = new();
}
DictionaryLayout provides three layouts:
| Value | Display |
|---|---|
TwoColumns |
Shows the key and value side by side |
OneColumnWithValueVisible |
Always shows the value below the key |
OneColumnWithValueFoldout |
Shows the value below the key in a Foldout |
The attribute defines the initial presentation. If a user changes the column width or layout in the Inspector, Unity stores that preference per property path and the user setting takes precedence afterward.
Choose Reset to Defaults from the column-header menu to return to the attribute's values.
For a shared configuration applied to the same closed Dictionary type, Unity also provides the assembly-level [DictionaryDisplayForType] attribute.
using System;
using System.Collections.Generic;
using UnityEngine;
[assembly: DictionaryDisplayForType(
typeof(Dictionary<string, SkillLevel>),
keyLabel = "Skill",
valueLabel = "Level",
layout = DictionaryLayout.OneColumnWithValueVisible)]
[Serializable]
public struct SkillLevel
{
public int rank;
public float bonus;
}
This is useful for nested Dictionaries where no directly annotated field exists. The target must be an exact closed Dictionary type, and the assembly declaring the attribute must itself define the key type, value type, or one of the nested types involved.
The restriction prevents one assembly from globally changing the display of a Dictionary made entirely from types it does not own. Consequently, an assembly cannot apply a global setting to a combination such as Dictionary<string, int> when both types come only from the .NET Base Class Library.
Because this attribute affects an entire assembly, reusable packages should also consider how broad that display policy becomes.
Prefab Overrides are tracked by serialized position, not by key
Unity 6.6 lets an individual Dictionary key or value be overridden in a Prefab instance. The override is not tracked by the logical key, however. It is recorded against the serialized entry position, effectively behaving like a List or array index.
The Inspector's displayed sort order can differ from the stored insertion order, so the row visible on screen does not necessarily correspond to the serialized position used by the override.
If entries are added or removed in the Prefab asset and serialized positions shift, an instance override may end up applying to a different key than the one originally intended. The official manual therefore warns developers to check instance overrides after editing the asset.
Practical precautions include:
- Move frequently edited shared tables into ScriptableObject assets.
- Check Prefab variants and instances after adding or removing entries in the Prefab asset.
- Review YAML diffs and the Overrides list rather than trusting only the visible Inspector order.
- Never assume that an override follows an entry merely because its key text remains the same.
- Avoid mixing Dictionary migration and unrelated Prefab changes in the same commit.
This is arguably the most important trap in the first version of the feature: the data structure is key-based, but Prefab Override tracking is not.
Use the serialization rules analyzer to catch unsupported shapes
Unity 6.6's analyzer reports several common Dictionary mistakes:
| Diagnostic | Meaning |
|---|---|
UAC1009 |
A Dictionary is placed directly inside an unsupported collection shape |
UAC1012 |
An interface or abstract type is used as the key or value |
UAC1013 |
An IEnumerable type is used as the key |
UAC1014 |
[SerializeReference] is applied to a Dictionary |
UAC1015 |
A public Dictionary does not have [SerializeField]
|
UAC1016 |
The key or value type is not serializable |
This makes it easier to catch the old failure mode where code compiles but the data is silently not persisted as expected.
Monitor these analyzer warnings in CI as well, and avoid suppressing Dictionary-related warnings without a deliberate reason.
Do custom SerializableDictionary implementations become unnecessary?
Unity's custom serialization documentation now recommends using the built-in serializer for ordinary Dictionaries and reserving callback-based conversion for collection types Unity still cannot handle directly.
For a new implementation, start with the built-in Dictionary when all of these conditions hold:
- The minimum supported version is Unity 6.6 or later.
- A plain
Dictionary<TKey, TValue>is sufficient. - Both key and value are within the supported range.
- The standard Inspector supports the editing workflow.
- The Prefab Override limitations are acceptable.
- There is no external serialized format that must remain stable.
Custom implementations still have a role when you need:
-
SortedDictionary,HashSet, or a Dictionary-derived type. - Normalization, compression, encryption, or ID conversion while saving.
- A fixed schema shared with tools outside Unity.
- Backward compatibility with older Unity versions.
- Custom duplicate merging, change notifications, or automatic key generation.
- Import from CSV or spreadsheets.
- Search, bulk editing, or specialized validation.
When an existing SerializableDictionary owns responsibilities beyond storage, replacing only its type mechanically is unsafe.
It is also too early to claim that the built-in implementation always loads several times faster or always creates smaller files. The largest guaranteed benefit is maintenance: less conversion code, fewer Drawers, fewer compatibility paths, and a smaller test surface.
Measure performance with your actual data when that difference matters.
Does Odin become unnecessary?
If a project uses Odin only to serialize and display ordinary Dictionaries, Unity 6.6 may let you reduce that dependency. Moving plain Dictionaries back to Unity's built-in serializer can reduce the number of serialization paths and package-specific behaviors the project must maintain.
Sirenix's own best-practice guidance also recommends leaving data to Unity's serializer when Unity can handle it and limiting Odin serialization to the places that require it.
Odin's value is much broader than Dictionary support:
- A wider range of types than Unity's built-in serializer supports.
- Interfaces and polymorphic data structures.
- Inspector grouping, buttons, and conditional display.
- Validation, custom Drawers, and editor tooling.
- Compatibility with existing Odin Serializer data.
When a project is deeply built around Odin attributes and editor workflows, removing the package only because Unity gained Dictionary support may provide little benefit. A practical hybrid is to use Unity's standard serializer for new, simple Dictionaries and keep Odin for complex types and advanced Inspector UI.
The dangerous migration order is to upgrade Unity, change the field types, and remove Odin before reading the old data. Once the old serialization backend is gone, those values may no longer be accessible.
Keep Odin operational while reading the old fields, copy the data into the new standard Dictionaries, save and reload every affected asset, and remove the dependency only after verification.
Migrating existing data safely
Do not assume that replacing a custom type or Odin-managed field with Dictionary<TKey, TValue> automatically migrates existing serialized data. The stored representation and serialization backend can differ, so the project needs an explicit migration step.
[FormerlySerializedAs] preserves an old field name. It is not a universal converter between unrelated types or serialization systems.
Recommended migration sequence
- Create a dedicated migration branch and a backup.
- Add the new standard Dictionary under a different field name while keeping the old field.
- Copy the old data into the new field with Editor-only code.
- Detect duplicates, null keys, missing references, and count differences.
- Save all affected ScriptableObjects, Prefabs, and Scenes.
- Reload scripts, restart the Editor, and test a Player build.
- Compare key counts, keys, and values between the old and new formats mechanically.
- Remove the old field, custom Drawer, or Odin dependency only after the comparison passes.
During migration, the fields may temporarily look like this:
[SerializeField, HideInInspector]
private LegacyStringIntDictionary legacyPrices = new();
[SerializeField]
private Dictionary<string, int> prices = new();
The following is a conceptual migration example:
#if UNITY_EDITOR
[ContextMenu("Migration/Copy legacy prices")]
private void CopyLegacyPrices()
{
var migrated = new Dictionary<string, int>();
bool hasError = false;
foreach (var pair in legacyPrices)
{
if (pair.Key == null)
{
Debug.LogError("Null key was found.", this);
hasError = true;
continue;
}
if (migrated.ContainsKey(pair.Key))
{
Debug.LogError($"Duplicate key: {pair.Key}", this);
hasError = true;
continue;
}
migrated.Add(pair.Key, pair.Value);
}
if (hasError)
{
Debug.LogError("Migration was aborted.", this);
return;
}
UnityEditor.Undo.RecordObject(this, "Migrate item prices");
prices = migrated;
UnityEditor.EditorUtility.SetDirty(this);
}
#endif
Replace LegacyStringIntDictionary and its enumeration API with the types used by your own project.
A Context Menu may be enough for a handful of assets. Large migrations should use a dedicated tool based on APIs such as AssetDatabase, PrefabUtility.LoadPrefabContents, and explicit Scene opening and closing so that no asset is skipped.
Put a large migration tool in an Editor folder or an Editor-only assembly definition so that UnityEditor references cannot leak into a Player assembly. The sample fully qualifies UnityEditor.Undo and UnityEditor.EditorUtility and wraps its body in #if UNITY_EDITOR, but separating the whole tool into an Editor assembly is safer.
Seeing correct values in memory immediately after the copy is not enough. Restart the Editor, reopen the Scenes and Prefabs, and confirm that a Player build reconstructs the same values.
For an Odin migration, keeping Odin installed until the old fields have been read successfully is especially important.
Do not assume JsonUtility and network JSON are the same feature
Unity 6.6's Dictionary support is primarily about Unity serialization for Scenes, Prefabs, ScriptableObjects, and similar Unity data.
The JsonUtility Scripting API says that it processes the same supported types as Unity's standard serializer, which can be read as implying Dictionary support. At the same time, the Unity 6.6 JSON Serialization manual available on July 15, 2026 still contains the older statement that Dictionary<> is unsupported.
Because those official pages are inconsistent, this article does not treat JsonUtility Dictionary support as a settled guarantee.
Even when a Beta build appears to work, test the exact target version for:
- A complete
ToJsonandFromJsonround trip, including the actual JSON shape produced. - Compatibility with external API schemas and existing save data.
- Behavior in an IL2CPP Player build.
- Null keys, duplicates, and custom key types.
“Unity can persist this internally” and “this is an appropriate long-term save or network contract” are different claims.
For external formats, choose the tool that fits the contract: Newtonsoft.Json, MessagePack, dedicated DTOs, or another deliberate schema may still be the better choice.
Practical decision table
| Scenario | Recommendation |
|---|---|
| New Unity 6.6+ project with simple ID lookup data | Start with the built-in Dictionary |
| Active Unity 6.0–6.5 production project | Do not upgrade to a Beta only for Dictionary support |
| Hundreds or thousands of static game-data records | The Dictionary may be a storage target, but use an external tool or dedicated Editor for authoring |
| Asset Store package or shared package that supports older Unity versions | Keeping the existing implementation can be reasonable |
| Project already uses Odin extensively | Move only new, simple Dictionaries first and migrate incrementally |
| Prefab instances frequently override entries | Understand position-based overrides and consider moving the table to a ScriptableObject |
Dictionary serialization does not provide type-safe code generation, referential-integrity checks, localization workflows, server sharing, or live data-update infrastructure. It fills a major gap in Unity serialization; it is not a complete game-data platform.
Adoption checklist
- [ ] Record whether the target Unity version is a Beta or a final release.
- [ ] Add
[SerializeField]to every Dictionary that should be persisted. - [ ] Confirm that the declared type is exactly
Dictionary<TKey, TValue>. - [ ] Confirm that both key and value are supported types.
- [ ] Implement
EqualsandGetHashCodefor custom keys. - [ ] Remove all duplicate and null keys.
- [ ] Remember that Inspector sorting and serialized order are different.
- [ ] Recheck variants and instances after changing a Prefab asset.
- [ ] Confirm that the lack of Multi-Object Editing and search is acceptable, or provide an alternative.
- [ ] Test Scene reload, Domain Reload, and Editor restart.
- [ ] Test a Player build on every relevant target platform.
- [ ] Compare migrated counts and contents mechanically.
- [ ] Round-trip test
JsonUtilityin the exact target version when you use it. - [ ] Do not remove old fields or Odin before migration verification passes.
Summary
Unity 6.6 allows a standard Dictionary<TKey, TValue> marked with [SerializeField] to be stored in Unity assets and edited through the Inspector. Built-in sorting, duplicate and null warnings, and configurable layouts greatly reduce the need to build a serialization backend and Drawer merely because a project needs key-based lookup data.
Important boundaries remain:
-
[SerializeField]is required even on public Dictionary fields. - Only the exact
Dictionary<TKey, TValue>declared type uses the new backend. - Interfaces and abstract types are generally unsupported.
- A Dictionary cannot be placed directly inside a List or array.
- Multi-Object Editing, search, and filtering are unavailable in the Beta.
- Prefab Overrides follow serialized positions rather than logical keys.
- Existing custom and Odin formats require explicit migration.
- The Unity 6.6 Beta documentation is inconsistent about
JsonUtilitysupport.
So, do custom implementations and Odin become unnecessary?
For the sole purpose of saving a plain Dictionary and editing it in the standard Inspector, they will be unnecessary in many Unity 6.6+ projects.
For unsupported types, advanced Inspector workflows, older Unity versions, custom external formats, or compatibility with existing data, they remain necessary.
In new projects, the built-in Dictionary should become the first option. In existing projects, do not delete a working serialization system merely because the Editor gained a built-in alternative. Inventory the old system's responsibilities, migrate explicitly, reload everything, and verify the result before reducing dependencies.
References
- Unity Beta Program
- Unity 6.6 Manual: Dictionary serialization
- Unity 6.6 Manual: Serialization rules analyzer
- Unity 6.6 Manual: Custom serialization
- Unity 6.6 Scripting API: DictionaryDisplayAttribute
- Unity 6.6 Scripting API: DictionaryDisplayForTypeAttribute
- Unity 6.6 Scripting API: DictionaryLayout
- Unity 6.6 Scripting API: JsonUtility.ToJson
- Unity 6.6 Scripting API: JsonUtility.FromJson
- Unity 6.6 Manual: JSON Serialization
- Unity 6.5 Manual: Serialization rules
- Odin Inspector: Serializing Dictionaries
- Odin Serializer: Features and Limitations
- Odin Serializer: Best Practices
Top comments (0)