com.unity.serialization - writing a custom subclass of Dictionary<> ?

I started using the com.unity.serialization experimental package and was happy to find that it will serialize any Dictionary<T1, T2> just fine. Finally! :slight_smile:

But then I hit a roadbump: once a dictionary is subclassed, it no longer serializes. Like this example (the base class itself serializes just fine):

[Serializable] public class SubDict : Dictionary<long, Tile3D> {}

For compatibility reasons (Unity 2021.2 is my minimum version target) I’m using package version 2.1.2.

I thought it’s probably because the serializer cannot infer types of keys and values anymore, and it’ll probably just require writing an adapter … but oh boy, that is NOT straightforward! I thought it should be as simple as looping through the KeyValueCollection and just calling a method for both the key and the value. Possibly even just casting “this as Dictionary<long, Tile3D>” and passing that into some SerializeObject method.

But first, this is the expected output that serializing the Dictionary<long, Tile3D> produces:

[
    {
        "Key": 1,
        "Value": {
            "Index": 0,
            "Flags": 0
        }
    },
    {
        "Key": 2,
        "Value": {
            "Index": 0,
            "Flags": 0
        }
    }
]

I’ve experimented for an hour going through various combinations of the context.Writer methods. This is as far as I got:

[
    {
        Key = 1
    }
    {
        Key = 2
    }
]

And that’s the code that produces this with 2 items in the dict (it’s written using the Simplified flag to make the json more readable, thus there aren’t any quotes):

            public void Serialize(in JsonSerializationContext<SubDict> context, SubDict dict)
            {
                context.Writer.WriteBeginArray();
                foreach (var kvp in dict)
                {
                    context.Writer.WriteBeginObject();
                    context.Writer.WriteKeyValue("Key", kvp.Key);
                    context.Writer.WriteEndObject();
                }
                context.Writer.WriteEndArray();
            }

I’m hoping someone can nudge me in the right direction! A few things I wish to understand:

  • is this the right way to go about serializing a subclass of a Dictionary<>? Especially considering that I am only subclassing in order to make the code more readable & maintainable. The fallback solution is to just add the dictionary as a field.
  • how to write Key/Value with colons rather than equals (Key:1 vs Key=1)?

Note: missing commas appear to be because of the “Simplified” flag.

Hi @CodeSmile ! Sorry for the long respond time, I was not aware of this thread.

That is correct, the reflection-based and the source generated property bags check for collection types in a way that makes it not work with derived types. I believe this should be supported. Can you report the issue for the Properties module? Thanks!

You almost had it with your snippet, you are missing one line to serialize the value:context.SerializeValue("Value", kvp.Value);

This should be able to get what you are looking for:

public void Serialize(in JsonSerializationContext<SubDict> context, SubDict dict)
{
    context.Writer.WriteBeginArray();
    foreach (var kvp in dict)
    {
        context.Writer.WriteBeginObject();
        context.Writer.WriteKeyValue("Key", kvp.Key);
        context.SerializeValue("Value", kvp.Value);
        context.Writer.WriteEndObject();
    }
    context.Writer.WriteEndArray();
}

I’m not very knowledgeable about simplified json, but I believe that when using sjson, the equal sign should be used instead of the colon. While you can probably write the adapter in a way that it would use a colon, the result might not pass sjson validation and as such, I would not recommend it.

Turning off the simplified mode should make it use the colons again.

Hope this helps!

2 Likes