Closed timheuer closed 10 months ago
The reasons it’s not coming in as an array is because it isn’t an array. It’s a mapping. Which means it will be properties or key/value pairs. You could probably create your own node deserializer or type converter to handle that part of your yaml differently.
You can see here what the different parts of your yaml are with this cool tool. It’s what we use to determine the validity of yaml and one of the tools we use to make sure we’re parsing things correctly.
I think the short answer to your question is "no" since arrays don't have string keys (like reason
) but you could make a type/named tuple that can hold the key and value from the mapping in one value.
It seems a lot easier to deserialize the way the library intends and then just implement a method on your type that returns the array-ified (key, value) pairs. I made a quick version with F#, but the C# version should be broadly similar. Except more verbose 😉.
result:
[| { name = "reason"
value =
{ description = "The reason for the manual run"
kind = "string"
required = true } }
{ name = "something_else"
value =
{ description = "Some other input"
kind = "boolean"
required = false } } |]
Ok, spent some time on this, you can use a custom INodeDeserializer
to convert it into an array. I set the value to the Key
property on the object. Here's the code.
Note, it does not deserialize to the same yaml, if you need that, let me know and I could try and put something together for that.
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
var yaml = @"name: ""Build""
on:
workflow_dispatch:
inputs:
reason:
description: The reason for the manual run
required: true
type: string
something_else:
description: Some other input
required: false
type: boolean
";
var deserializer = new DeserializerBuilder().WithNodeDeserializer(new InputObjectNodeDeserializer(), (syntax) => syntax.OnTop()).Build();
var action = deserializer.Deserialize<Actions>(yaml);
foreach (var input in action.On.WorkflowDispatch.Inputs)
{
Console.WriteLine("{0} - {1}", input.Key, input.Description);
}
public class Actions
{
[YamlMember(Alias = "name")]
public string Name { get; set; }
[YamlMember(Alias = "on")]
public Dispatch On { get; set; }
}
public class Dispatch
{
[YamlMember(Alias = "workflow_dispatch")]
public WorkflowDispatch WorkflowDispatch { get; set; }
}
public class WorkflowDispatch
{
[YamlMember(Alias = "inputs")]
public Input[] Inputs { get; set; }
}
public class Input
{
[YamlMember(Alias = "key")]
public string Key { get; set; }
[YamlMember(Alias = "description")]
public string Description { get; set; }
[YamlMember(Alias = "required")]
public bool Required { get; set; }
[YamlMember(Alias = "type")]
public string Type { get; set; }
}
public class InputObjectNodeDeserializer : INodeDeserializer
{
public bool Deserialize(IParser reader, Type expectedType, Func<IParser, Type, object?> nestedObjectDeserializer, out object? value)
{
if (expectedType != typeof(Input[]))
{
value = null;
return false;
}
if (!reader.Accept<MappingStart>())
{
value = null;
return false;
}
reader.Consume<MappingStart>();
var result = new List<Input>();
while (!reader.TryConsume<MappingEnd>(out var _))
{
var keyScalar = reader.Consume<Scalar>();
var input = nestedObjectDeserializer(reader, typeof(Input)) as Input;
if (input != null)
{
input.Key = keyScalar.Value;
}
result.Add(input);
}
value = result.ToArray();
return true;
}
}
The output
reason - The reason for the manual run
something_else - Some other input
I'm going to close this issue since this is an example of doing what you want, re-open if you need serialization too.
I'm trying to parse a GitHub Actions workflow file...here's a snippet of valid workflow:
The
inputs
is not resolving to an array, but rather properties of inputs. If I change these to-reason
etc then it sees them as an array, but that's not valid workflow syntax.I'm curious if there is a way I can get the parser to see this as an array where the
reason
portion would be the key?