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

How to prevent Serializer to change value types : "2" => 2 #428

Closed
jrouaix opened this issue Aug 21, 2019 · 9 comments
Closed

How to prevent Serializer to change value types : "2" => 2 #428

jrouaix opened this issue Aug 21, 2019 · 9 comments

Comments

@jrouaix
Copy link

jrouaix commented Aug 21, 2019

Hello,

I have hard time trying to change json swagger to yaml

image

Please what is the correct SerializerBuilder magic trick to disable yaml serializer changing types behavior ?

@jrouaix
Copy link
Author

jrouaix commented Aug 21, 2019

The problem here is the swagger property value should be the string "2.0" and not the number 2.0

@aaubry
Copy link
Owner

aaubry commented Aug 21, 2019

There is no semantic difference in YAML between 2.0 and "2.0". Both are alternative representations of the same scalar. If you really want quotes, you can annotate your property with YamlMemberAttribute and set the ScalarStyle property.

@aaubry
Copy link
Owner

aaubry commented Aug 21, 2019

To further clarify, types in YAML are indicated using tags. Having quotes has no influence on the type.

@jrouaix
Copy link
Author

jrouaix commented Aug 22, 2019

Well I understand that it doesn't matter for YAML, but it matters for the swagger editor :

image

Also I cannot make a propertly typed .net object with YamlMemberAttribute to handle this as my original json is made from dynamic transformations in the jObject itself.

Do you see an entrypoint in your library where I can plug the behavior "if it's a string put quotes"

@aaubry
Copy link
Owner

aaubry commented Aug 22, 2019

You should be able to achieve what you want by registering an IEventEmitter that overrides the style of scalars. I can't provide the exact code because I am away from my computer, but it would be similar to the example from the documentation:

https://github.com/aaubry/YamlDotNet/wiki/Serialization.Serializer#witheventemitter

Override the method for scalars and if needed check the event info to see if the value was a string. Let me know if this helps.

@jrouaix
Copy link
Author

jrouaix commented Aug 27, 2019

Ok, this did the job, thank you! :

class RespectTypesEventEmitter : ChainedEventEmitter
{
    public RespectTypesEventEmitter(IEventEmitter nextEmitter) : base(nextEmitter) { }

    public override void Emit(ScalarEventInfo eventInfo, IEmitter emitter)
    {
        if(eventInfo.Source.Type == typeof(string))
        {
            eventInfo.Style = ScalarStyle.DoubleQuoted;
        }
        base.Emit(eventInfo, emitter);
    }
}

Now everything is "typeish_correct" (event if it should not matter in yaml ok)

image

Do you see a way I can have the property names non quotes, and only the values quoted ? I don't see enough info in ScalarEventInfo to tackle this problem.

But for now, it's ok, swagger does not complain anymore.

@aaubry
Copy link
Owner

aaubry commented Aug 28, 2019

The only thing that comes to my mind would be to also override the Emit() methods that take MappingStartEventInfo, MappingEndEventInfo, SequenceStartEventInfo and SequenceEndEventInfo and track whether the current scalar is a value or a key.

Here is a possible implementation:

class ForceQuotedStringValuesEventEmitter : ChainedEventEmitter
{
    private class EmitterState
    {
        private int valuePeriod;
        private int currentIndex;
        
        public EmitterState(int valuePeriod)
        {
            this.valuePeriod = valuePeriod;
        }
        
        public bool VisitNext()
        {
            ++currentIndex;
            return (currentIndex % valuePeriod) == 0;
        }
    }
    
    private readonly Stack<EmitterState> state = new Stack<EmitterState>();
    
    public ForceQuotedStringValuesEventEmitter(IEventEmitter nextEmitter)
        : base(nextEmitter)
    {
        this.state.Push(new EmitterState(1));
    }

    public override void Emit(ScalarEventInfo eventInfo, IEmitter emitter)
    {
        if (this.state.Peek().VisitNext())
        {
            if(eventInfo.Source.Type == typeof(string))
            {
                eventInfo.Style = ScalarStyle.DoubleQuoted;
            }
        }
            
        base.Emit(eventInfo, emitter);
    }

    public override void Emit(MappingStartEventInfo eventInfo, IEmitter emitter)
    {
        this.state.Peek().VisitNext();
        this.state.Push(new EmitterState(2));
        base.Emit(eventInfo, emitter);
    }

    public override void Emit(MappingEndEventInfo eventInfo, IEmitter emitter)
    {
        this.state.Pop();
        base.Emit(eventInfo, emitter);
    }

    public override void Emit(SequenceStartEventInfo eventInfo, IEmitter emitter)
    {
        this.state.Peek().VisitNext();
        this.state.Push(new EmitterState(1));
        base.Emit(eventInfo, emitter);
    }

    public override void Emit(SequenceEndEventInfo eventInfo, IEmitter emitter)
    {
        this.state.Pop();
        base.Emit(eventInfo, emitter);
    }
}

You can see it in action here: https://dotnetfiddle.net/lTZ8rm

@jrouaix
Copy link
Author

jrouaix commented Aug 29, 2019

thanks @aaubry ! a dream come true !

@S3v3Nice
Copy link

S3v3Nice commented Jul 6, 2022

If you serialize only IDictionary<string, object>, you can use a more elegant way to check if the current event refers to a value, not a property name. We need to put the condition eventInfo.Source.StaticType == typeof(object)

So the code will look like this:

private class ForceQuoteEventEmitter : ChainedEventEmitter
    {
        public ForceQuoteEventEmitter(IEventEmitter nextEmitter) : base(nextEmitter)
        {
        }

        public override void Emit(ScalarEventInfo eventInfo, IEmitter emitter)
        {
            if (eventInfo.Source.Type == typeof(string) && eventInfo.Source.StaticType == typeof(object))
            {
                eventInfo.Style = ScalarStyle.DoubleQuoted;
            }
            
            base.Emit(eventInfo, emitter);
        }
    }

That's all, I hope it will be useful to someone :)

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

No branches or pull requests

3 participants