2

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?

Chill
  • 77
  • 11

2 Answers2

2

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

greybeard
  • 2,249
  • 8
  • 30
  • 66
Antoine Aubry
  • 12,203
  • 10
  • 45
  • 74
  • Thanks Antoine - that seems like a much nicer way of doing it! – Chill Jun 21 '16 at 11:57
  • This would be great to put on the YamlDotNet documentation page. – redcurry Aug 24 '16 at 13:05
  • I created a BitmapConverter because I wanted to read/write Bitmaps. Everything works. But I'm wondering what would happen if I also wanted to read/write another byte-array type. How would the deserializer know that one kind of byte-array is a Bitmap but another of a different type? I had to register the tag mapping of "tag:yaml.org,2002:binary" to typeof(Bitmap). Does that mean that any byte array will be interpreted as a Bitmap? – redcurry Aug 24 '16 at 15:26
  • `deserializer.RegisterTagMapping()` and `deserializer.RegisterTypeConverter()` where marked as ***Obsolete***, and now are not accesible @antoine-aubry, can you provide if decorator or other new way to accomplish this? – TrustworthySystems Aug 07 '21 at 23:03
0

For anyone that's interested.... I fixed this by creating the string myself and adding the !!binary tag, and also doing some clean up. Below is the code.

ToYaml:

IDictionary<string, string> data = new Dictionary<string, string>();
string byteAsBase64Fromat = Convert.ToBase64String("The string to convert");
byteAsBase64Fromat = "!!binary |-\n" + byteAsBase64Fromat + "\n";

data.Add(ValueKey, byteAsBase64Fromat);
string yaml;
using (var output = new StringWriter())
{
  var serializer = new Serializer();
  serializer.Serialize(output, data);

   yaml = output.ToString();
}
string yaml = yaml.Replace(">", "");
return yaml.Replace(Environment.NewLine + Environment.NewLine,     Environment.NewLine);

And then back by:

string binaryText = ((YamlScalarNode)data.Children[new YamlScalarNode(ValueKey)]).Value
String value = Convert.FromBase64String(binaryText);
EricSchaefer
  • 25,272
  • 21
  • 67
  • 103
Chill
  • 77
  • 11