Skip to content

Commit

Permalink
Support for 'default'. Refactors and simplifies validation a bit to be
Browse files Browse the repository at this point in the history
more flexible.
  • Loading branch information
atombender committed Oct 2, 2018
1 parent cd8720c commit c7be101
Show file tree
Hide file tree
Showing 22 changed files with 157 additions and 1,868 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ While not finished, go-jsonschema can be used today. Aside from some minor featu
- Validation ([RFC draft](http://json-schema.org/latest/json-schema-validation.html))
- [ ] Schema annotations (§10)
- [x] `description`
- [ ] `default`
- [x] `default` (only for struct fields)
- [ ] `readOnly`
- [ ] `writeOnly`
- [ ] ~~`title`~~ (N/A)
Expand Down
40 changes: 12 additions & 28 deletions pkg/codegen/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,6 @@ type Type interface {
IsNillable() bool
}

type RuledType interface {
GetRequiredRules() []Rule
}

type PointerType struct {
Type Type
}
Expand All @@ -182,12 +178,6 @@ func (a ArrayType) Generate(out *Emitter) {
a.Type.Generate(out)
}

func (ArrayType) GetRequiredRules() []Rule {
return []Rule{
ArrayNotEmpty{},
}
}

type NamedType struct {
Package *Package
Decl *TypeDecl
Expand Down Expand Up @@ -237,7 +227,8 @@ func (EmptyInterfaceType) Generate(out *Emitter) {
}

type StructType struct {
Fields []StructField
Fields []StructField
RequiredJSONFields []string
}

func (StructType) IsNillable() bool { return false }
Expand All @@ -251,31 +242,24 @@ func (s *StructType) Generate(out *Emitter) {
out.Indent(1)
i := 0
for _, f := range s.Fields {
if !f.Synthetic {
if i > 0 {
out.Newline()
}
f.Generate(out)
if i > 0 {
out.Newline()
i++
}
f.Generate(out)
out.Newline()
i++
}
out.Indent(-1)
out.Print("}")
}

type StructField struct {
Name string
Type Type
Comment string
Tags string
JSONName string
Synthetic bool
Rules []Rule
}

func (f *StructField) AddRule(rule Rule) {
f.Rules = append(f.Rules, rule)
Name string
Type Type
Comment string
Tags string
JSONName string
DefaultValue interface{}
}

func (f *StructField) Generate(out *Emitter) {
Expand Down
25 changes: 0 additions & 25 deletions pkg/codegen/validation.go

This file was deleted.

118 changes: 62 additions & 56 deletions pkg/generator/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"sort"
"strings"

"github.com/sanity-io/litter"

"github.com/atombender/go-jsonschema/pkg/codegen"
"github.com/atombender/go-jsonschema/pkg/schemas"
)
Expand Down Expand Up @@ -354,49 +356,59 @@ func (g *schemaGenerator) generateDeclaredType(
g.output.file.Package.AddDecl(&decl)

if structType, ok := theType.(*codegen.StructType); ok {
g.output.file.Package.AddImport("encoding/json", "")
g.output.file.Package.AddDecl(&codegen.Method{
Impl: func(out *codegen.Emitter) {
out.Comment("UnmarshalJSON implements json.Unmarshaler.")
out.Println("func (j *%s) UnmarshalJSON(b []byte) error {", decl.Name)
out.Indent(1)
out.Println("var v struct {")
out.Indent(1)
fields := append([]codegen.StructField{}, structType.Fields...)
for _, f := range structType.Fields {
if f.Synthetic {
f.Generate(out)
out.Newline()
}
needUnmarshal := false
if len(structType.RequiredJSONFields) > 0 {
needUnmarshal = true
} else {
for _, f := range structType.Fields {
if f.DefaultValue != nil {
needUnmarshal = true
break
}
out.Indent(-1)
out.Println("}")
out.Println("if err := json.Unmarshal(b, &v); err != nil { return err }")
for _, f := range fields {
if f.Synthetic {
for _, r := range f.Rules {
r.GenerateValidation(out, fmt.Sprintf("v.%s", f.Name),
fmt.Sprintf("field %s", f.JSONName))
}
}
}
if needUnmarshal {
if len(structType.RequiredJSONFields) > 0 {
g.output.file.Package.AddImport("fmt", "")
}
g.output.file.Package.AddImport("encoding/json", "")
g.output.file.Package.AddDecl(&codegen.Method{
Impl: func(out *codegen.Emitter) {
out.Comment("UnmarshalJSON implements json.Unmarshaler.")
out.Println("func (j *%s) UnmarshalJSON(b []byte) error {", decl.Name)
out.Indent(1)
out.Println("var %s map[string]interface{}", varNameRawMap)
out.Println("if err := json.Unmarshal(b, &%s); err != nil { return err }",
varNameRawMap)
for _, f := range structType.RequiredJSONFields {
out.Println(`if v, ok := %s["%s"]; !ok || v == nil {`, varNameRawMap, f)
out.Indent(1)
out.Println(`return fmt.Errorf("field %s: required")`, f)
out.Indent(-1)
out.Println("}")
}
}
out.Println("type plain %s", decl.Name)
out.Println("var p plain")
out.Println("if err := json.Unmarshal(b, &p); err != nil { return err }")
for _, f := range fields {
if !f.Synthetic {
for _, r := range f.Rules {
r.GenerateValidation(out, fmt.Sprintf("p.%s", f.Name),
fmt.Sprintf("field %s", f.JSONName))

out.Println("type Plain %s", decl.Name)
out.Println("var %s Plain", varNamePlainStruct)
out.Println("if err := json.Unmarshal(b, &%s); err != nil { return err }",
varNamePlainStruct)
for _, f := range structType.Fields {
if f.DefaultValue != nil {
out.Println(`if v, ok := %s["%s"]; !ok || v == nil {`, varNameRawMap, f.JSONName)
out.Indent(1)
out.Println(`%s.%s = %s`, varNamePlainStruct, f.Name, litter.Sdump(f.DefaultValue))
out.Indent(-1)
out.Println("}")
}
}
}
out.Println("*j = %s(p)", decl.Name)
out.Println("return nil")
out.Indent(-1)
out.Println("}")
},
})

out.Println("*j = %s(%s)", decl.Name, varNamePlainStruct)
out.Println("return nil")
out.Indent(-1)
out.Println("}")
},
})
}
}

return &codegen.NamedType{Decl: &decl}, nil
Expand Down Expand Up @@ -514,23 +526,12 @@ func (g *schemaGenerator) generateStructType(
return nil, fmt.Errorf("could not generate type for field %q: %s", name, err)
}

if isRequired {
if rt, ok := structField.Type.(codegen.RuledType); ok {
g.output.file.Package.AddImport("fmt", "") // All rules need fmt
for _, r := range rt.GetRequiredRules() {
structField.AddRule(r)
}
}
if !structField.Type.IsNillable() {
g.output.file.Package.AddImport("fmt", "") // All rules need fmt
syntheticField := structField
syntheticField.Comment = ""
syntheticField.Synthetic = true
syntheticField.Type = codegen.PointerType{Type: syntheticField.Type}
syntheticField.AddRule(codegen.NilStructFieldRequired{})
structType.AddField(syntheticField)
}
} else if !structField.Type.IsNillable() {
if prop.Default != nil {
structField.DefaultValue = prop.Default
} else if isRequired {
structType.RequiredJSONFields = append(structType.RequiredJSONFields, structField.JSONName)
} else {
// Optional, so must be pointer
structField.Type = codegen.PointerType{Type: structField.Type}
}

Expand Down Expand Up @@ -754,3 +755,8 @@ func (ns nameScope) add(s string) nameScope {
result[len(result)-1] = s
return result
}

var (
varNamePlainStruct = "plain"
varNameRawMap = "raw"
)
26 changes: 6 additions & 20 deletions tests/data/core/4.2.1_array.go.output
Original file line number Diff line number Diff line change
@@ -1,35 +1,21 @@
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.

package test
import "encoding/json"

type 421ArrayMyObjectArrayElem map[string]interface{}
type 421Array struct {
// MyBooleanArray corresponds to the JSON schema field "myBooleanArray".
MyBooleanArray []bool `json:"myBooleanArray,omitempty"`
MyBooleanArray *[]bool `json:"myBooleanArray,omitempty"`

// MyNullArray corresponds to the JSON schema field "myNullArray".
MyNullArray []interface{} `json:"myNullArray,omitempty"`
MyNullArray *[]interface{} `json:"myNullArray,omitempty"`

// MyNumberArray corresponds to the JSON schema field "myNumberArray".
MyNumberArray []float64 `json:"myNumberArray,omitempty"`
MyNumberArray *[]float64 `json:"myNumberArray,omitempty"`

// MyObjectArray corresponds to the JSON schema field "myObjectArray".
MyObjectArray []421ArrayMyObjectArrayElem `json:"myObjectArray,omitempty"`
MyObjectArray *[]421ArrayMyObjectArrayElem `json:"myObjectArray,omitempty"`

// MyStringArray corresponds to the JSON schema field "myStringArray".
MyStringArray []string `json:"myStringArray,omitempty"`
}

// UnmarshalJSON implements json.Unmarshaler.
func (j *421Array) UnmarshalJSON(b []byte) error {
var v struct {
}
if err := json.Unmarshal(b, &v); err != nil { return err }
type plain 421Array
var p plain
if err := json.Unmarshal(b, &p); err != nil { return err }
*j = 421Array(p)
return nil
}

MyStringArray *[]string `json:"myStringArray,omitempty"`
}
66 changes: 10 additions & 56 deletions tests/data/core/object.go.output
Original file line number Diff line number Diff line change
@@ -1,39 +1,8 @@
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.

package test
import "encoding/json"
import "fmt"

type 421ArrayMyObjectArrayElem map[string]interface{}
type 421Array struct {
// MyBooleanArray corresponds to the JSON schema field "myBooleanArray".
MyBooleanArray []bool `json:"myBooleanArray,omitempty"`

// MyNullArray corresponds to the JSON schema field "myNullArray".
MyNullArray []interface{} `json:"myNullArray,omitempty"`

// MyNumberArray corresponds to the JSON schema field "myNumberArray".
MyNumberArray []float64 `json:"myNumberArray,omitempty"`

// MyObjectArray corresponds to the JSON schema field "myObjectArray".
MyObjectArray []421ArrayMyObjectArrayElem `json:"myObjectArray,omitempty"`

// MyStringArray corresponds to the JSON schema field "myStringArray".
MyStringArray []string `json:"myStringArray,omitempty"`
}

// UnmarshalJSON implements json.Unmarshaler.
func (j *421Array) UnmarshalJSON(b []byte) error {
var v struct {
}
if err := json.Unmarshal(b, &v); err != nil { return err }
type plain 421Array
var p plain
if err := json.Unmarshal(b, &p); err != nil { return err }
*j = 421Array(p)
return nil
}

import "encoding/json"

type ObjectMyObject struct {
// MyString corresponds to the JSON schema field "myString".
Expand All @@ -42,35 +11,20 @@ type ObjectMyObject struct {

// UnmarshalJSON implements json.Unmarshaler.
func (j *ObjectMyObject) UnmarshalJSON(b []byte) error {
var v struct {
MyString *string `json:"myString"`
var raw map[string]interface{}
if err := json.Unmarshal(b, &raw); err != nil { return err }
if v, ok := raw["myString"]; !ok || v == nil {
return fmt.Errorf("field myString: required")
}
if err := json.Unmarshal(b, &v); err != nil { return err }
if v.MyString == nil {
return fmt.Errorf("field myString: must be set")
}
type plain ObjectMyObject
var p plain
if err := json.Unmarshal(b, &p); err != nil { return err }
*j = ObjectMyObject(p)
type Plain ObjectMyObject
var plain Plain
if err := json.Unmarshal(b, &plain); err != nil { return err }
*j = ObjectMyObject(plain)
return nil
}


type Object struct {
// MyObject corresponds to the JSON schema field "myObject".
MyObject *ObjectMyObject `json:"myObject,omitempty"`
}

// UnmarshalJSON implements json.Unmarshaler.
func (j *Object) UnmarshalJSON(b []byte) error {
var v struct {
}
if err := json.Unmarshal(b, &v); err != nil { return err }
type plain Object
var p plain
if err := json.Unmarshal(b, &p); err != nil { return err }
*j = Object(p)
return nil
}

}
Loading

0 comments on commit c7be101

Please sign in to comment.