Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pass ScalarStyle to members of Dictionary #478

Open
nj0yeh opened this issue Apr 2, 2020 · 3 comments
Open

Pass ScalarStyle to members of Dictionary #478

nj0yeh opened this issue Apr 2, 2020 · 3 comments

Comments

@nj0yeh
Copy link

nj0yeh commented Apr 2, 2020

The ScalarStyle of a Dictionary is not used for members of the Dictionary.

        [YamlMember(ScalarStyle = ScalarStyle.DoubleQuoted)]
        public Dictionary<string, object> EnvironmentVariables { get; set; }

The problem seems to be that in TraverseDictionary there is a call to GetObjectDescriptor that creates a new ObjectDescriptor with ScalarStyle = ScalarStyle.Any.

@aaubry
Copy link
Owner

aaubry commented Apr 19, 2020

The problem with this is that each element of a Dictionary will potentially produce two scalars. One for the key and another for the value. This means that, the following dictionary:

new Dictionary<string, object> {
  { "one", 1 },
  { "two", 2 }
}

would serialize as:

"one": "1"
"two": "2"

While this may be desirable in some cases, I'm sure that many people would want to control the scalar style of the keys and values independently.

Also, it may not be appropriate to use the YamlMember attribute to control how children are serialized. Currently the attribute applies only to the member to which it is applied. Maybe we need a different attribute to control how keys and values are emitted in dictionaries. What do you think ?

@nj0yeh
Copy link
Author

nj0yeh commented Apr 19, 2020

Yes, in fact it is not that easy. What we needed is quotes around every string value in a Dictionary<string, object>, which can again contain Dictionary<string, object>s or Lists.

We hard-coded it by putting this code in TraverseDictionary after var value = GetObjectDescriptor(entry.Value, valueType);:

if (entry.Value is string)
{
    value.ScalarStyle = ScalarStyle.DoubleQuoted;
}

We did the same in TraverseList:

foreach (var item in (IEnumerable)value.NonNullValue())
{
	var listItemValue = GetObjectDescriptor(item, itemType);
	if (item is string)
	{
		listItemValue.ScalarStyle = ScalarStyle.DoubleQuoted;
	}

	Traverse(index, listItemValue, visitor, context, path);
	++index;
}

Of course we had to make ScalarStyle public settable. Perhaps it would make sense to create a global serializer settings to achieve this behaviour. It makes a big difference if the values are quoted or not e. g. for Helm (https://helm.sh/).

@dynamiquel
Copy link

This is just what I needed. Thanks.
It would be nice if I didn't need to edit the source to do this. I'm using dynamics and so I guess the deserialiser can't figure out what's a string or a number.

I then just used a custom INodeTypeResolver to assume that any scalar with the DoubleQuoted style is a string.

private class ScalarNodeTypeResolver : INodeTypeResolver
{
    bool INodeTypeResolver.Resolve(NodeEvent nodeEvent, ref Type currentType)
    {
        if (currentType != typeof(object))
            return false;

        if (nodeEvent is Scalar scalar)
        {
            if (scalar.Style == ScalarStyle.DoubleQuoted)
            {
                currentType = typeof(string);
                return true;
            }
        }
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants