YamlDotNet — Пользовательская сериализация

У меня есть класс .NET, который представляет вызов метода RPC, например:

class MethodCall
{
    public string MethodName { get; set; }
    public Collection<object> Arguments { get; set; }
}

Я хочу сериализовать Collection<MethodCall> в YAML. Для этого я использую YamlDotNet.

По умолчанию YamlDotNet сериализует эти объекты следующим образом:

methodName: someName
arguments:
- arg1
- arg2
- ...

Я хотел бы упростить полученный YAML до:

someName:
- arg1
- arg2

Есть ли простой способ добиться этого? Обратите внимание, что аргументы могут быть сложными объектами (т. е. не простыми скалярами).


person Frederik Carlier    schedule 07.10.2020    source источник
comment
Выше public Collection‹object› Аргументы { get; набор; } добавьте следующее: [XmlElement()] У вас есть массив, и Net автоматически добавляет два тега для массивов. Использование Element добавляет только один тег.   -  person jdweng    schedule 07.10.2020


Ответы (1)


Этого можно добиться, зарегистрировав реализацию IYamlTypeConverter, которая выполняет необходимое преобразование.

Вот возможная реализация:

public sealed class MethodCallConverter : IYamlTypeConverter
{
    // Unfortunately the API does not provide those in the ReadYaml and WriteYaml
    // methods, so we are forced to set them after creation.
    public IValueSerializer ValueSerializer { get; set; }
    public IValueDeserializer ValueDeserializer { get; set; }
    
    public bool Accepts(Type type) => type == typeof(MethodCall);

    public object ReadYaml(IParser parser, Type type)
    {
        parser.Consume<MappingStart>();

        var call = new MethodCall
        {
            MethodName = (string)ValueDeserializer.DeserializeValue(parser, typeof(string), new SerializerState(), ValueDeserializer),
            Arguments = (Collection<object>)ValueDeserializer.DeserializeValue(parser, typeof(Collection<object>), new SerializerState(), ValueDeserializer),
        };

        parser.Consume<MappingEnd>();
        
        return call;
    }
    
    public void WriteYaml(IEmitter emitter, object value, Type type)
    {
        emitter.Emit(new MappingStart());

        var call = (MethodCall)value;
        ValueSerializer.SerializeValue(emitter, call.MethodName, typeof(string));
        ValueSerializer.SerializeValue(emitter, call.Arguments, typeof(Collection<object>));

        emitter.Emit(new MappingEnd());
    }
}

Преобразователь необходимо зарегистрировать в SerializerBuilder и DeserializerBuilder методом WithTypeConverter. Обратите внимание, что YamlDotNet не предоставляет возможности рекурсивного вызова (де)сериализатора, поэтому в качестве обходного пути мы должны установить некоторые общедоступные свойства. Это не так чисто, как могло бы быть, но все же работает:

string SerializeMethodCall(MethodCall call)
{
    var methodCallConverter = new MethodCallConverter();
    var serializerBuilder = new SerializerBuilder()
        .WithNamingConvention(CamelCaseNamingConvention.Instance)
        .WithTypeConverter(methodCallConverter);

    methodCallConverter.ValueSerializer = serializerBuilder.BuildValueSerializer();

    var serializer = serializerBuilder.Build();

    var yaml = serializer.Serialize(call);
    return yaml;
}

MethodCall DeserializeMethodCall(string yaml)
{
    var methodCallConverter = new MethodCallConverter();
    var deserializerBuilder = new DeserializerBuilder()
        .WithNamingConvention(CamelCaseNamingConvention.Instance)
        .WithTypeConverter(methodCallConverter);

    methodCallConverter.ValueDeserializer = deserializerBuilder.BuildValueDeserializer();

    var deserializer = deserializerBuilder.Build();
    var call = deserializer.Deserialize<MethodCall>(yaml);
    return call;
}
person Antoine Aubry    schedule 08.10.2020
comment
Есть ли способ переопределить сериализацию встроенных типов? Например, у меня есть пара целых чисел, которые я хотел бы записать в виде шестнадцатеричной строки, а не целого числа. Похоже, я могу использовать IYamlTypeConverter для переопределения поведения, но тогда оно будет применяться ко всем целым числам, а не к конкретным свойствам, которые я хочу записать в шестнадцатеричном формате. В настоящее время нет способа узнать, какое свойство вы сериализуете, только тип. - person Chris Delpire; 10.03.2021