omissis / go-jsonschema

A tool to generate Go data types from JSON Schema definitions.
MIT License
574 stars 90 forks source link

A generated `AdditionalProperties` doesn't have a struct tag #242

Open jamietanna opened 4 months ago

jamietanna commented 4 months ago

At least for JSON and YAML, I'd expect:

                    "additionalProperties": {
                        "type": "string"
                    }

Would convert into the following Go:

    AdditionalProperties map[string]string `json:"-"`

But instead we see:

    AdditionalProperties map[string]string

Which means that (un)marshaling fails to capture the required fields

omissis commented 4 months ago

Hey @jamietanna, thanks for opening this issue. I am not sure I understand this use-case, could you elaborate a bit more please? eg: why would additional properties need to be omitted? and how does including them causes the failure in capturing the required fields?

Perhaps a full marshaling+unmarshaling example could help, thanks! 😁

apodznoev commented 3 months ago

I'm facing the same problem, no additionalProperties are being unmarshalled, rendering them unusable.

E.g. having a schema.json

{
    "type": "object",
    "additionalProperties": {
        "type": "object",
        "properties": {
            "property1": {
                "type": "string"
            },
            "property2": {
                "type": "number"
            }
        }
    },
    "properties": {
        "foo": {
            "type": "string"
        },
        "bar": {
            "type": "string"
        }
    }
}

after calling go-jsonschema schema.json --output schema.go --package gen --tags yaml --extra-imports

the Go code is:

// Code generated by github.com/atombender/go-jsonschema, DO NOT EDIT.

package gen

import "encoding/json"
import yaml "gopkg.in/yaml.v3"

type TestJson struct {
    // Bar corresponds to the JSON schema field "bar".
    Bar *string `yaml:"bar,omitempty"`

    // Foo corresponds to the JSON schema field "foo".
    Foo *string `yaml:"foo,omitempty"`

    AdditionalProperties map[string]interface{}
}

// UnmarshalJSON implements json.Unmarshaler.
func (j *TestJson) UnmarshalJSON(b []byte) error {
    var raw map[string]interface{}
    if err := json.Unmarshal(b, &raw); err != nil {
        return err
    }
    type Plain TestJson
    var plain Plain
    if err := json.Unmarshal(b, &plain); err != nil {
        return err
    }
    if v, ok := raw[""]; !ok || v == nil {
        plain.AdditionalProperties = map[string]interface{}{}
    }
    *j = TestJson(plain)
    return nil
}

// UnmarshalYAML implements yaml.Unmarshaler.
func (j *TestJson) UnmarshalYAML(value *yaml.Node) error {
    var raw map[string]interface{}
    if err := value.Decode(&raw); err != nil {
        return err
    }
    type Plain TestJson
    var plain Plain
    if err := value.Decode(&plain); err != nil {
        return err
    }
    if v, ok := raw[""]; !ok || v == nil {
        plain.AdditionalProperties = map[string]interface{}{}
    }
    *j = TestJson(plain)
    return nil
}

Then a test unmarshalling the following test.yaml:

foo: "foo value"
bar: "bar value"
property1: "additional property 1"
property2: "additional property 2"

fails since "property1" and "property2" are not read into Go struct.

Go test code:

t.Run("test", func(t *testing.T) {
        var yamlNode yaml3.Node
        file, _ := os.ReadFile("test.yaml")
        yaml3.Unmarshal(file, &yamlNode)

        genStruct := gen.TestJson{}

        genStruct.UnmarshalYAML(&yamlNode)
        if len(genStruct.AdditionalProperties) == 0 {
            t.Fatalf("no additional properties")
        }
    })
omissis commented 2 weeks ago

hey @jamietanna @apodznoev I pushed this pr out, mind to have a look at it and see if it resolves the problems you noted? #278 thanks!