Search code examples
yamldotnet

YamlDotNet !!binary type


I am trying to send binary data, i.e. byte arrays, using yaml. According to the yaml documentation, Yaml Binary Type, this is supported. On the Java side I use SnakeYaml and if a value of byte[] is passed, then the yaml correctly gives !!binary.

This functionality does not seem to be supported "out of the box" in YamlDotNet. The below code snippet creates a sequence of integer values:

IDictionary<string, object> data = new Dictionary<string, object>();

        const string value = ":| value: <XML> /n\n C:\\cat";
        byte[] bytes = Encoding.UTF8.GetBytes(value);
        data.Add(ValueKey, bytes);


        // Turn the object representation into text
        using (var output = new StringWriter())
        {
            var serializer = new Serializer();
            serializer.Serialize(output, data);

            return output.ToString();
        }

Output like:

val:\r- 58\r- 124\r- 32\r- 118\r- 97\r- 108\r- 117\r- 101\r- 58\r- 32\r- 60\r- 88\r- 77\r- 76\r- 62\r- 32\r- 47\r- 110\r- 10\r- 32\r- 67\r- 58\r- 92\r- 99\r- 97\r- 116\r

But I would like something more like:

  val: !!binary |-
OnwgdmFsdWU6IDxYTUw+IC9uCiBDOlxjYXQ=

Can anyone recommend a workaround?


Solution

  • The preferred way to add support for custom types is to use a custom IYamlTypeConverter. A possible implementation for the !!binary type would be:

    public class ByteArrayConverter : IYamlTypeConverter
    {
        public bool Accepts(Type type)
        {
            // Return true to indicate that this converter is able to handle the byte[] type
            return type == typeof(byte[]);
        }
    
        public object ReadYaml(IParser parser, Type type)
        {
            var scalar = (YamlDotNet.Core.Events.Scalar)parser.Current;
            var bytes = Convert.FromBase64String(scalar.Value);
            parser.MoveNext();
            return bytes;
        }
    
        public void WriteYaml(IEmitter emitter, object value, Type type)
        {
            var bytes = (byte[])value;
            emitter.Emit(new YamlDotNet.Core.Events.Scalar(
                null,
                "tag:yaml.org,2002:binary",
                Convert.ToBase64String(bytes),
                ScalarStyle.Plain,
                false,
                false
            ));
        }
    }
    

    To use the converter in the Serializer, you simply need to register it using the following code:

    var serializer = new Serializer();
    serializer.RegisterTypeConverter(new ByteArrayConverter());
    

    For the Deserializer, you also need to register the converter, but you also need to add a tag mapping to resolve the !!binary tag to the byte[] type:

    var deserializer = new Deserializer();
    deserializer.RegisterTagMapping("tag:yaml.org,2002:binary", typeof(byte[]));
    deserializer.RegisterTypeConverter(new ByteArrayConverter());
    

    A fully working example can be tried here