mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
Add AbstractDictionarySerializer (#4276)
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Serialization.Markdown;
|
||||
using Robust.Shared.Serialization.Markdown.Mapping;
|
||||
using Robust.Shared.Serialization.Markdown.Validation;
|
||||
using Robust.Shared.Serialization.Markdown.Value;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
||||
|
||||
namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
|
||||
/// <summary>
|
||||
/// A custom type serializer for reading a set of types that inherit from some base type.
|
||||
/// </summary>
|
||||
public sealed class AbstractDictionarySerializer<TValue> : ITypeSerializer<Dictionary<Type, TValue>, MappingDataNode>
|
||||
where TValue : notnull
|
||||
{
|
||||
public ValidationNode Validate(ISerializationManager serializationManager, MappingDataNode node,
|
||||
IDependencyCollection dependencies, ISerializationContext? context = null)
|
||||
{
|
||||
var mapping = new Dictionary<ValidationNode, ValidationNode>();
|
||||
foreach (var (keyNode, valueNode) in node.Children)
|
||||
{
|
||||
if (keyNode is not ValueDataNode key)
|
||||
{
|
||||
mapping.Add(new ErrorNode(keyNode, $"Expected {nameof(ValueDataNode)} but was {keyNode.GetType()}"), new ValidatedValueNode(valueNode));
|
||||
continue;
|
||||
}
|
||||
var type = serializationManager.ReflectionManager.YamlTypeTagLookup(typeof(TValue), key.Value);
|
||||
if (type == null)
|
||||
{
|
||||
mapping.Add(new ErrorNode(keyNode, $"Could not resolve type: {key.Value}"), new ValidatedValueNode(valueNode));
|
||||
continue;
|
||||
}
|
||||
|
||||
mapping.Add(new ValidatedValueNode(key), serializationManager.ValidateNode(type, valueNode, context));
|
||||
}
|
||||
|
||||
return new ValidatedMappingNode(mapping);
|
||||
}
|
||||
|
||||
public Dictionary<Type, TValue> Read(ISerializationManager serializationManager, MappingDataNode node, IDependencyCollection dependencies,
|
||||
SerializationHookContext hookCtx, ISerializationContext? context = null, ISerializationManager.InstantiationDelegate<Dictionary<Type, TValue>>? instanceProvider = null)
|
||||
{
|
||||
var dict = instanceProvider != null ? instanceProvider() : new Dictionary<Type, TValue>();
|
||||
foreach (var (keyNode, valueNode) in node.Children)
|
||||
{
|
||||
var key = (ValueDataNode) keyNode;
|
||||
var type = serializationManager.ReflectionManager.YamlTypeTagLookup(typeof(TValue), key.Value)!;
|
||||
var value = (TValue) serializationManager.Read(type, valueNode, hookCtx, context, notNullableOverride:true)!;
|
||||
dict.Add(type, value);
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
public DataNode Write(ISerializationManager serializationManager, Dictionary<Type, TValue> value, IDependencyCollection dependencies,
|
||||
bool alwaysWrite = false, ISerializationContext? context = null)
|
||||
{
|
||||
var mappingNode = new MappingDataNode();
|
||||
|
||||
foreach (var (key, val) in value)
|
||||
{
|
||||
mappingNode.Add(
|
||||
serializationManager.WriteValue(key.Name, alwaysWrite, context, notNullableOverride:true),
|
||||
serializationManager.WriteValue(key, val, alwaysWrite, context));
|
||||
}
|
||||
|
||||
return mappingNode;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Serialization.Markdown;
|
||||
using Robust.Shared.Serialization.Markdown.Mapping;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.Serialization.TypeSerializers.Custom;
|
||||
|
||||
[TestFixture]
|
||||
[TestOf(typeof(AbstractDictionarySerializer<>))]
|
||||
public sealed class AbstractDictionarySerializerTest : RobustUnitTest
|
||||
{
|
||||
private const string TestYaml = @"
|
||||
SealedTestTypeA:
|
||||
x: 1
|
||||
y: 2
|
||||
SealedTestTypeB:
|
||||
x: 3
|
||||
z: 4
|
||||
";
|
||||
|
||||
[Test]
|
||||
public void SerializationTest()
|
||||
{
|
||||
var seri = IoCManager.Resolve<ISerializationManager>();
|
||||
seri.Initialize();
|
||||
|
||||
var stream = new YamlStream();
|
||||
stream.Load(new StringReader(TestYaml));
|
||||
var node = (MappingDataNode)stream.Documents[0].RootNode.ToDataNode();
|
||||
|
||||
var validation = seri.ValidateNode<Dictionary<Type, AbstractTestData>,
|
||||
MappingDataNode,
|
||||
AbstractDictionarySerializer<AbstractTestData>>
|
||||
(node);
|
||||
Assert.That(validation.GetErrors().Count(), Is.EqualTo(0));
|
||||
|
||||
var data = seri.Read<Dictionary<Type, AbstractTestData>,
|
||||
MappingDataNode,
|
||||
AbstractDictionarySerializer<AbstractTestData>>
|
||||
(node, notNullableOverride:true);
|
||||
Assert.NotNull(data);
|
||||
Assert.That(data.Count, Is.EqualTo(2));
|
||||
Assert.That(data.ContainsKey(typeof(SealedTestTypeA)));
|
||||
Assert.That(data.ContainsKey(typeof(SealedTestTypeB)));
|
||||
|
||||
var a = data[typeof(SealedTestTypeA)] as SealedTestTypeA;
|
||||
var b = data[typeof(SealedTestTypeB)] as SealedTestTypeB;
|
||||
|
||||
Assert.NotNull(a);
|
||||
Assert.NotNull(b);
|
||||
|
||||
Assert.That(a!.X, Is.EqualTo(1));
|
||||
Assert.That(a.Y, Is.EqualTo(2));
|
||||
Assert.That(b!.X, Is.EqualTo(3));
|
||||
Assert.That(b.Z, Is.EqualTo(4));
|
||||
|
||||
var newNode = (MappingDataNode)seri.WriteValue<Dictionary<Type, AbstractTestData>,
|
||||
AbstractDictionarySerializer<AbstractTestData>>
|
||||
(data, notNullableOverride:true);
|
||||
|
||||
Assert.Null(node.Except(newNode));
|
||||
validation = seri.ValidateNode<Dictionary<Type, AbstractTestData>,
|
||||
MappingDataNode,
|
||||
AbstractDictionarySerializer<AbstractTestData>>
|
||||
(newNode);
|
||||
Assert.That(validation.GetErrors().Count(), Is.EqualTo(0));
|
||||
}
|
||||
|
||||
[ImplicitDataDefinitionForInheritors]
|
||||
private abstract class AbstractTestData
|
||||
{
|
||||
[DataField("x")] public int X;
|
||||
}
|
||||
|
||||
private sealed class SealedTestTypeA : AbstractTestData
|
||||
{
|
||||
[DataField("y")] public int Y;
|
||||
}
|
||||
|
||||
private sealed class SealedTestTypeB : AbstractTestData
|
||||
{
|
||||
[DataField("z")] public int Z;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user