Skip to content

Commit 5d8158f

Browse files
authored
Additional Properties do not get unmarshalled (#278)
* feat: introduce support for unmarshalling additional properties in json * feat: introduce support for unmarshalling additional properties in yaml * chore: fix linting issues
1 parent c7cb03d commit 5d8158f

23 files changed

+448
-19
lines changed

go.sum

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
2-
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b h1:XxMZvQZtTXpWMNWK82vdjCLCe7uGMFXdTsJH0v3Hkvw=
32
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
43
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
54
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -28,7 +27,6 @@ github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQ
2827
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
2928
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
3029
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
31-
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0 h1:GD+A8+e+wFkqje55/2fOVnZPkoDIu1VooBWfNrnY8Uo=
3230
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
3331
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
3432
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@@ -39,7 +37,6 @@ github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
3937
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
4038
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
4139
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
42-
github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312 h1:UsFdQ3ZmlzS0BqZYGxvYaXvFGUbCmPGy8DM7qWJJiIQ=
4340
github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
4441
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
4542
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
@@ -57,6 +54,7 @@ golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
5754
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
5855
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY=
5956
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
57+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
6058
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
6159
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
6260
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

go.work.sum

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@ github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvSc
44
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
55
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
66
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
7-
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
8-
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
97
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
8+
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
109
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
1110
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
1211
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=

pkg/generator/generate.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,8 @@ func (g *Generator) findOutputFileForSchemaID(id string) (*output, error) {
175175

176176
func (g *Generator) beginOutput(
177177
id string,
178-
outputName, packageName string,
178+
outputName,
179+
packageName string,
179180
) (*output, error) {
180181
if packageName == "" {
181182
return nil, fmt.Errorf("%w: %q", errMapURIToPackageName, id)

pkg/generator/json_formatter.go

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,7 @@ func (jf *jsonFormatter) generate(declType codegen.TypeDecl, validators []valida
3636

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

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

55+
if structType, ok := declType.Type.(*codegen.StructType); ok {
56+
for _, f := range structType.Fields {
57+
if f.Name == additionalProperties {
58+
out.Printlnf("st := reflect.TypeOf(Plain{})")
59+
out.Printlnf("for i := range st.NumField() {")
60+
out.Indent(1)
61+
out.Printlnf("delete(raw, st.Field(i).Name)")
62+
out.Printlnf("delete(raw, strings.Split(st.Field(i).Tag.Get(\"json\"), \",\")[0])")
63+
out.Indent(-1)
64+
out.Printlnf("}")
65+
out.Printlnf("if err := mapstructure.Decode(raw, &plain.AdditionalProperties); err != nil {")
66+
out.Indent(1)
67+
out.Printlnf("return err")
68+
out.Indent(-1)
69+
out.Printlnf("}")
70+
71+
break
72+
}
73+
}
74+
}
75+
5676
out.Printlnf("*j = %s(%s)", declType.Name, varNamePlainStruct)
5777
out.Printlnf("return nil")
5878
out.Indent(-1)

pkg/generator/schema_generator.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,12 @@ func (g *schemaGenerator) generateDeclaredType(
240240

241241
for _, f := range structType.Fields {
242242
if f.DefaultValue != nil {
243+
if f.Name == additionalProperties {
244+
g.output.file.Package.AddImport("reflect", "")
245+
g.output.file.Package.AddImport("strings", "")
246+
g.output.file.Package.AddImport("github.com/go-viper/mapstructure/v2", "")
247+
}
248+
243249
validators = append(validators, &defaultValidator{
244250
jsonName: f.JSONName,
245251
fieldName: f.Name,
@@ -633,10 +639,11 @@ func (g *schemaGenerator) generateStructType(
633639

634640
structType.AddField(
635641
codegen.StructField{
636-
Name: "AdditionalProperties",
642+
Name: additionalProperties,
637643
DefaultValue: defaultValue,
638644
SchemaType: &schemas.Type{},
639645
Type: fieldType,
646+
Tags: "mapstructure:\",remain\"",
640647
},
641648
)
642649
}

pkg/generator/utils.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77
"github.com/atombender/go-jsonschema/pkg/schemas"
88
)
99

10+
const additionalProperties = "AdditionalProperties"
11+
1012
func sortPropertiesByName(props map[string]*schemas.Type) []string {
1113
names := make([]string, 0, len(props))
1214
for name := range props {

pkg/generator/yaml_formatter.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,27 @@ func (yf *yamlFormatter) generate(declType codegen.TypeDecl, validators []valida
5353
v.generate(out)
5454
}
5555

56+
if structType, ok := declType.Type.(*codegen.StructType); ok {
57+
for _, f := range structType.Fields {
58+
if f.Name == "AdditionalProperties" {
59+
out.Printlnf("st := reflect.TypeOf(Plain{})")
60+
out.Printlnf("for i := range st.NumField() {")
61+
out.Indent(1)
62+
out.Printlnf("delete(raw, st.Field(i).Name)")
63+
out.Printlnf("delete(raw, strings.Split(st.Field(i).Tag.Get(\"json\"), \",\")[0])")
64+
out.Indent(-1)
65+
out.Printlnf("}")
66+
out.Printlnf("if err := mapstructure.Decode(raw, &plain.AdditionalProperties); err != nil {")
67+
out.Indent(1)
68+
out.Printlnf("return err")
69+
out.Indent(-1)
70+
out.Printlnf("}")
71+
72+
break
73+
}
74+
}
75+
}
76+
5677
out.Printlnf("*j = %s(%s)", declType.Name, varNamePlainStruct)
5778
out.Printlnf("return nil")
5879
out.Indent(-1)

tests/data/core/additionalProperties/arrayAdditionalProperties.go

Lines changed: 12 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/data/core/additionalProperties/boolAdditionalProperties.go

Lines changed: 12 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/data/core/additionalProperties/intAdditionalProperties.go

Lines changed: 12 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)