0

I am trying to format the name of property of an object according to value I am getting in the output of my yaml file. I am using yamldotnet library. Below are my classes

State.cs

public class State
    {
        public string name { get; set; }
        public List<Operation> operations { get; set; }

    }

Operation.cs

public class Operation
    {
        public string name { get; set; }
        public string type { get; set; }
    }

In the state class, I have list of operations. After serializing, I am getting the following yaml output:

states:
- name: uPError
  operations:
  - name: switch on uP
    type: entry
  - name: switch off uP
    type: exit
  - name: test Do
    type: do

But the format I intend to display is as follows:

states:
- name: uPError
  entryActions: [switch on uP]
  exitActions: [switch off uP]
  doActions: [test Do]

I am getting the types and their name, based on the type, I need to change it to entryAction, exitActions or doActions and then append the corresponding name. How can I achieve the above intended format? Thanks in advance.

Priom Biswas
  • 85
  • 2
  • 12

1 Answers1

0

One way of achieving this is to implement IYamlTypeConverter to customize the conversion of the State type. This gives you control over the whole serialization of your class:

public class StateConverter : IYamlTypeConverter
{
    public bool Accepts(Type type)
    {
        return type == typeof(State);
    }

    public object? ReadYaml(IParser parser, Type type)
    {
        var state = new State { operations = new List<UserQuery.Operation>() };
        
        parser.Consume<MappingStart>();
        while (!parser.TryConsume<MappingEnd>(out var _))
        {
            var key = parser.Consume<Scalar>().Value;
            if (key == nameof(state.name))
            {
                state.name = parser.Consume<Scalar>().Value;
            }
            else
            {
                parser.Consume<SequenceStart>();
                while (!parser.TryConsume<SequenceEnd>(out var _))
                {
                    state.operations.Add(new Operation
                    {
                       type = key,
                       name = parser.Consume<Scalar>().Value,
                    });
                }
            }
        }
        
        return state;
    }

    public void WriteYaml(IEmitter emitter, object? value, Type type)
    {
        var state = (State)value!;
        
        emitter.Emit(new MappingStart());
        
        emitter.Emit(new Scalar(nameof(state.name)));
        emitter.Emit(new Scalar(state.name));

        foreach (var operationGroup in state.operations.GroupBy(o => o.type))
        {
            emitter.Emit(new Scalar(operationGroup.Key));

            emitter.Emit(new SequenceStart(null, null, false, SequenceStyle.Flow));
            foreach (var operation in operationGroup)
            {
                emitter.Emit(new Scalar(operation.name));
            }
            emitter.Emit(new SequenceEnd());
        }

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

You need to register that type on the SerializerBuilder and DeserializerBuilder:

var serializer = new SerializerBuilder()
    .WithTypeConverter(new StateConverter())
    .Build();
var deserializer = new DeserializerBuilder()
    .WithTypeConverter(new StateConverter())
    .Build();
Antoine Aubry
  • 12,203
  • 10
  • 45
  • 74