Skip to content

fix: YAML, Add deserialization Handler for DeletedKeyWithId#3203

Open
Eideren wants to merge 1 commit into
stride3d:masterfrom
Eideren:deleted_id
Open

fix: YAML, Add deserialization Handler for DeletedKeyWithId#3203
Eideren wants to merge 1 commit into
stride3d:masterfrom
Eideren:deleted_id

Conversation

@Eideren
Copy link
Copy Markdown
Collaborator

@Eideren Eideren commented Jun 3, 2026

PR Details

Some definition to ensure the rest makes sense, here's how an entry for an item in a collection looks like in YAML:

Materials:
    4f2a44a83ccd0b9a1d6f8d3f5f0772d2~0: d9db4834-f7ce-4f6a-be87-d54732302ec3:Ground Material

4f2a44a83ccd0b9a1d6f8d3f5f0772d2~0 is a KeyWithId, 4f2a44a83ccd0b9a1d6f8d3f5f0772d2 would be the Id, while 0 is the key.

Deserializing a collections whose items defined on the base prefab have been removed would lead to a warning when loading in a project;

FormatException: The input string '' was not in a correct format.
   at System.Number.ThrowFormatException[TChar](ReadOnlySpan`1 value)
   at System.Int32.Parse(String s, IFormatProvider provider)
   at Stride.Core.Yaml.Serialization.Serializers.PrimitiveSerializer.ConvertFrom(ObjectContext& context, Scalar scalar) in I:\stride\sources\core\Stride.Core.Yaml\Serialization\Serializers\PrimitiveSerializer.cs:line 133
   at Stride.Core.Yaml.KeyWithIdSerializer.ConvertFrom(ObjectContext& objectContext, Scalar fromScalar) in I:\stride\sources\assets\Stride.Core.Assets\Yaml\KeyWithIdSerializer.cs:line 42

It's fairly easy to repro;

  • Create a prefab with a model
  • Add the prefab to a scene
  • Enable the material toggle on the model component of the prefab you added in the scene
  • Open the prefab itself and enable the material toggle on that prefab's model component

This is how the two yaml look like after this operation:
Base:

Materials:
    4f2a44a83ccd0b9a1d6f8d3f5f0772d2~0: d9db4834-f7ce-4f6a-be87-d54732302ec3:Ground Material

Derived:

Materials:
    4313c8fdf0379e96de405f63ca8da803*~0: d9db4834-f7ce-4f6a-be87-d54732302ec3:Ground Material
    4f2a44a83ccd0b9a1d6f8d3f5f0772d2~: ~(Deleted)

We might naively think that the deleted entry on the derived one doesn't make a ton of sense, but remember that we started by changing the derived one before changing the base one. Given that specificity, index 0 on the derived one should not inherit any changes from index #0 on its base. That would break user expectations as something overriden on derived should never be affected by changes on the base.

What's going on: KeyWithIdSerializer.ConvertFrom() calls PrimitiveSerializer.ConvertFrom() which in turn throws as the keyString provided is empty here

var context = new ObjectContext(objectContext.SerializerContext, null, keyDescriptor);
var key = scalarKeySerializer.ConvertFrom(ref context, new Scalar(keyString));
var result = Activator.CreateInstance(typeof(KeyWithId<>).MakeGenericType(keyType), id, key);

The ConvertTo method of this same class does not output a key when the value is a removal;

objectContext.Instance = key;
if (key.IsDeleted)
return $"{itemIdPart}~";
var keyString = scalarKeySerializer.ConvertTo(ref context);

Looks like that particular implementation wasn't entirely finished, so I added in logic in ConvertFrom to mirror ConvertTo's behavior.

Related Issue

Types of changes

  • Docs change / refactoring / dependency upgrade
  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist

  • My change requires a change to the documentation.
  • I have added tests to cover my changes.
  • All new and existing tests passed.
  • I have built and run the editor to try this change out.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant