YamlDotNet
YamlDotNet copied to clipboard
Stream style parsing returns strings for nulls
YamlDotNet 16.3.0
Describe the bug
I'm not very experienced with YAML, but (according to references like https://ref.coddy.tech/yaml/yaml-null-values) my understanding is that a key like foo: should produce a null value, not an empty string.
While the typed deserializer behaves accordingly, YamlStream (and company) interpret these null-like forms as strings, not null. This includes:
key:→""(should benull)key: null→"null"(should benull)key: ~→"~"(should benull)
To Reproduce
The following tests demonstrate the issue. The first test uses the typed deserializer and behaves correctly. The second uses YamlStream and fails:
[Fact]
public void TypedDeserializer_ParsesKeyWithNoValueAsNull()
{
// Arrange
var yaml = "Key:";
var deserializer = new DeserializerBuilder().Build();
// Act
var result = deserializer.Deserialize<Dummy>(yaml);
// Assert
Assert.Null(result.Key);
}
private class Dummy { public string? Key { get; set; } }
[Fact]
public void YamStream_ParsesKeyWithNoValueAsNull()
{
// Arrange
var yaml = "Key:";
var stream = new YamlStream();
stream.Load(new StringReader(yaml));
// Act
var root = (YamlMappingNode)stream.Documents[0].RootNode;
var node = root.Children[new YamlScalarNode("Key")];
var scalar = Assert.IsType<YamlScalarNode>(node);
// Assert
// This will fail — scalar.Value is "" instead of null
Assert.Null(scalar.Value);
}
I'm currently mitigating this in my conversion from YamlDotNet output to my own object by explicitly converting the strings returned with this switch case:
YamlScalarNode scalar when new[] { "", "~", "null" }.Contains(scalar.Value) && scalar.Style == ScalarStyle.Plain => null
This lets actual quoted empty strings through as strings. It handles my known cases, but I might be missing others. I assume the intention was not to return those as strings, particularly since the typed deserializer returns null.
That looks almost exactly the same as what we do in the other places we check if it’s null. Those strings and if it’s a plain scalar. Might be worth adding a property that says isnull or something to the scalar class.
I was naively expecting that scalar.Value itself would actually be null. But maybe true null just can't "fit" in there? I hadn't considered that.
I have my workaround (or, maybe it's just "doing the necessary") so lifecycle this issue as you see fit. I just wanted to report it since it surprised me.