go-yaml / yaml

YAML support for the Go language.
Other
6.84k stars 1.04k forks source link

Head comment becomes foot comment after unmarshal #610

Open danniel1205 opened 4 years ago

danniel1205 commented 4 years ago

I am testing with HeadComment from a very simple code below:

package main

import (
    "fmt"
    "gopkg.in/yaml.v3"
)

type Person struct {
    Name *yaml.Node
}

func main() {
    nameNode := &yaml.Node{
        Kind:        yaml.ScalarNode,
        HeadComment: "The name of a person",
        Value:       "Lily",
        Style: yaml.DoubleQuotedStyle,
    }

    person := Person{
        Name: nameNode,
    }

    dataInBytes, _ := yaml.Marshal(person)
    fmt.Println(string(dataInBytes))
}

I am expecting to see the output like this:

# The name of a person
name: "Lily"

However, I got:

name: "Lily"
# The name of a person

A head comment becomes a foot comment. Is this a bug ?

niemeyer commented 4 years ago

The issue here is that the expectation has a header on the key of the outer map, while the actual data has a header on an internal value. That said, the end result is indeed not great either way. Let me think a bit and see how we might improve this.

niemeyer commented 4 years ago

If the issue I explained isn't clear, I suggest unmarshaling that expected document into a yaml.Node, and pretty-printing the result. It'll be more clear where that comment ends up in a usual structure.

blami commented 4 years ago

@danniel1205 asked this on StackOverflow where I tried to reply why this happens https://stackoverflow.com/questions/61741453/why-golang-yaml-head-comment-becomes-foot-comment-after-unmarshal

One possible solution could be moving head comment of value in simple key: value ahead of the whole key: value so you can still have nice outer struct that you marshal (not so logical but handy). If there's decision made and help wanted with this one I'll be happy to help and contribute.

niemeyer commented 4 years ago

We need to think a bit deeper about the behavior in these cases. There are a few different factors to take into account while addressing it:

I appreciate any help, but part of the reason some of these issues take a while is that we need to think them through.

danniel1205 commented 4 years ago

Thanks @niemeyer @blami . Now I am clear on why head comment does not work in my case. Constructing a mappingNode for each struct field is definitely a workaround. However it makes the key in field tag useless, because it will be overwritten. Just thinking that supporting inject the comment via field tag would be a good idea?

niemeyer commented 4 years ago

@danniel1205 Yeah, I was thinking about something along those lines as well. Need some spare time to think though all the potential problems of doing so, but feel free to give it a go if you're interested.

siredwin commented 3 years ago

What is the status of this? I am dealing with a situation where I only can use a struct for documentation. So far I found this way to be the best. It would really be easier if I didn't have to use a map to achieve this because I don't need a map. Is there a way to get a comment before struct key so far? If there was a way to create a floating(uncommitted) comment that can be placed anywhere, that could also fix it. Another way would probably be to have a "hidden value" comment.

Email:
    # Email to send results
    Email: "email@gmail.com"
ClientToken:
    # Create a client to get a client token 
    ClientToken: "my token"
HostServer:
    # Name of message server. Default is "ws://mysite.com" 
    HostServer: "ws://mysite.com"
niemeyer commented 3 years ago

Yeah, this is what HeadComment does. Just set it for the respective field and you should get what you want.

siredwin commented 3 years ago

@niemeyer, I actually needed those comments on the top key not on the node map. This code adds an additional step of decoding the struct node into a map. By the way, I am grateful for this V3 feature!

I needed something similar to this

# Email to send results
Email: "email@gmail.com"

# Create a client to get a client token 
ClientToken: "my token"

# Name of message server. Default is "ws://mysite.com"    
HostServer: "ws://mysite.com"