getkin / kin-openapi

OpenAPI 3.0 (and Swagger v2) implementation for Go (parsing, converting, validation, and more)
MIT License
2.63k stars 432 forks source link

Use goccy/go-yaml instead of invopop/yaml? #849

Open renom opened 1 year ago

renom commented 1 year ago

https://github.com/goccy/go-yaml The library preserves the order of fields when converting from YAML It should fix deepmap/oapi-codegen#458

sonu27 commented 9 months ago

@renom if I use yaml.Marshal(&base) (goccy) paths are empty.

If I do:

b, err := json.Marshal(&base)
    if err != nil {
        return fmt.Errorf("marshalling base: %w", err)
    }

    b, err = yaml.JSONToYAML(b)
    if err != nil {
        return fmt.Errorf("converting to yaml: %w", err)
    }

I get paths but order is lost. Am I using it wrong?

renom commented 9 months ago

I'm not sure if I understand your question correctly. I'll write what I've found out since I opened the issue.

  1. To preserve the order of fields, you need to use goccy's type MapSlice instead of a regular map: https://github.com/goccy/go-yaml/blob/master/yaml.go#L68

For example:

struct {
    Map map[string]string
}

Should become:

struct {
    Map yaml.MapSlice
}
  1. MapSlice is an ordered version of map[any]any. So if you wanna have an equivalent of something like map[string]int or map[string]struct{}, it isn't supported unless you convert it to MapSlice (an equivalent of map[any]any).
  2. I've created an issue that suggests supporting a generic MapSlice (e.g. MapSlice[string, SpecificMapValue]): https://github.com/goccy/go-yaml/issues/422 Still hasn't got a response though.
sonu27 commented 9 months ago

I can't use a map, I'm trying to convert an openapi type struct. e.g.

// T is the root of an OpenAPI v3 document
// See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#openapi-object
type T struct {
    Extensions map[string]interface{} `json:"-" yaml:"-"`

    OpenAPI      string               `json:"openapi" yaml:"openapi"` // Required
    Components   *Components          `json:"components,omitempty" yaml:"components,omitempty"`
    Info         *Info                `json:"info" yaml:"info"`   // Required
    Paths        *Paths               `json:"paths" yaml:"paths"` // Required
    Security     SecurityRequirements `json:"security,omitempty" yaml:"security,omitempty"`
    Servers      Servers              `json:"servers,omitempty" yaml:"servers,omitempty"`
    Tags         Tags                 `json:"tags,omitempty" yaml:"tags,omitempty"`
    ExternalDocs *ExternalDocs        `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"`

    visited visitedComponent
}

Which has it's own MarshalJSON method, which then I convert to yaml.

fenollp commented 8 months ago

I'm thinking generic MapSlice would be a better struct that the one I use in https://github.com/getkin/kin-openapi/pull/695

@renom can you share your experience using MapSlice? What do you think of using this as the go-to map[_]_ within the lib?