Skip to content

Commit

Permalink
Additional Properties do not get unmarshalled (#278)
Browse files Browse the repository at this point in the history
* feat: introduce support for unmarshalling additional properties in json

* feat: introduce support for unmarshalling additional properties in yaml

* chore: fix linting issues
  • Loading branch information
omissis authored Oct 26, 2024
1 parent c7cb03d commit 5d8158f
Show file tree
Hide file tree
Showing 23 changed files with 448 additions and 19 deletions.
4 changes: 1 addition & 3 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b h1:XxMZvQZtTXpWMNWK82vdjCLCe7uGMFXdTsJH0v3Hkvw=
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand Down Expand Up @@ -28,7 +27,6 @@ github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQ
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0 h1:GD+A8+e+wFkqje55/2fOVnZPkoDIu1VooBWfNrnY8Uo=
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
Expand All @@ -39,7 +37,6 @@ github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312 h1:UsFdQ3ZmlzS0BqZYGxvYaXvFGUbCmPGy8DM7qWJJiIQ=
github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
Expand All @@ -57,6 +54,7 @@ golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY=
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
3 changes: 1 addition & 2 deletions go.work.sum
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvSc
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
Expand Down
3 changes: 2 additions & 1 deletion pkg/generator/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,8 @@ func (g *Generator) findOutputFileForSchemaID(id string) (*output, error) {

func (g *Generator) beginOutput(
id string,
outputName, packageName string,
outputName,
packageName string,
) (*output, error) {
if packageName == "" {
return nil, fmt.Errorf("%w: %q", errMapURIToPackageName, id)
Expand Down
24 changes: 22 additions & 2 deletions pkg/generator/json_formatter.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,7 @@ func (jf *jsonFormatter) generate(declType codegen.TypeDecl, validators []valida

if forceBefore || len(beforeValidators) != 0 {
out.Printlnf("var %s map[string]interface{}", varNameRawMap)
out.Printlnf("if err := %s.Unmarshal(b, &%s); err != nil { return err }",
formatJSON, varNameRawMap)
out.Printlnf("if err := %s.Unmarshal(b, &%s); err != nil { return err }", formatJSON, varNameRawMap)
}

for _, v := range beforeValidators {
Expand All @@ -53,6 +52,27 @@ func (jf *jsonFormatter) generate(declType codegen.TypeDecl, validators []valida
v.generate(out)
}

if structType, ok := declType.Type.(*codegen.StructType); ok {
for _, f := range structType.Fields {
if f.Name == additionalProperties {
out.Printlnf("st := reflect.TypeOf(Plain{})")
out.Printlnf("for i := range st.NumField() {")
out.Indent(1)
out.Printlnf("delete(raw, st.Field(i).Name)")
out.Printlnf("delete(raw, strings.Split(st.Field(i).Tag.Get(\"json\"), \",\")[0])")
out.Indent(-1)
out.Printlnf("}")
out.Printlnf("if err := mapstructure.Decode(raw, &plain.AdditionalProperties); err != nil {")
out.Indent(1)
out.Printlnf("return err")
out.Indent(-1)
out.Printlnf("}")

break
}
}
}

out.Printlnf("*j = %s(%s)", declType.Name, varNamePlainStruct)
out.Printlnf("return nil")
out.Indent(-1)
Expand Down
9 changes: 8 additions & 1 deletion pkg/generator/schema_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,12 @@ func (g *schemaGenerator) generateDeclaredType(

for _, f := range structType.Fields {
if f.DefaultValue != nil {
if f.Name == additionalProperties {
g.output.file.Package.AddImport("reflect", "")
g.output.file.Package.AddImport("strings", "")
g.output.file.Package.AddImport("github.com/go-viper/mapstructure/v2", "")
}

validators = append(validators, &defaultValidator{
jsonName: f.JSONName,
fieldName: f.Name,
Expand Down Expand Up @@ -633,10 +639,11 @@ func (g *schemaGenerator) generateStructType(

structType.AddField(
codegen.StructField{
Name: "AdditionalProperties",
Name: additionalProperties,
DefaultValue: defaultValue,
SchemaType: &schemas.Type{},
Type: fieldType,
Tags: "mapstructure:\",remain\"",
},
)
}
Expand Down
2 changes: 2 additions & 0 deletions pkg/generator/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"github.com/atombender/go-jsonschema/pkg/schemas"
)

const additionalProperties = "AdditionalProperties"

func sortPropertiesByName(props map[string]*schemas.Type) []string {
names := make([]string, 0, len(props))
for name := range props {
Expand Down
21 changes: 21 additions & 0 deletions pkg/generator/yaml_formatter.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,27 @@ func (yf *yamlFormatter) generate(declType codegen.TypeDecl, validators []valida
v.generate(out)
}

if structType, ok := declType.Type.(*codegen.StructType); ok {
for _, f := range structType.Fields {
if f.Name == "AdditionalProperties" {
out.Printlnf("st := reflect.TypeOf(Plain{})")
out.Printlnf("for i := range st.NumField() {")
out.Indent(1)
out.Printlnf("delete(raw, st.Field(i).Name)")
out.Printlnf("delete(raw, strings.Split(st.Field(i).Tag.Get(\"json\"), \",\")[0])")
out.Indent(-1)
out.Printlnf("}")
out.Printlnf("if err := mapstructure.Decode(raw, &plain.AdditionalProperties); err != nil {")
out.Indent(1)
out.Printlnf("return err")
out.Indent(-1)
out.Printlnf("}")

break
}
}
}

out.Printlnf("*j = %s(%s)", declType.Name, varNamePlainStruct)
out.Printlnf("return nil")
out.Indent(-1)
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 12 additions & 1 deletion tests/data/core/additionalProperties/boolAdditionalProperties.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 12 additions & 1 deletion tests/data/core/additionalProperties/intAdditionalProperties.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"id": "https://example.com/empty_object_properties",
"type": "object",
"additionalProperties": {
"type": "object",
"properties": {
"property1": {
"type": "string"
},
"property2": {
"type": "number"
}
}
},
"properties": {
"foo": {
"type": "string"
},
"bar": {
"type": "string"
}
}
}
Loading

0 comments on commit 5d8158f

Please sign in to comment.