diff --git a/pkg/codegen/model.go b/pkg/codegen/model.go index ae56bffb..49dc7eaf 100644 --- a/pkg/codegen/model.go +++ b/pkg/codegen/model.go @@ -54,10 +54,17 @@ func (p *Package) AddDecl(d Decl) { } func (p *Package) hasDecl(d Decl) bool { + n1, ok1 := d.(Named) + for _, pd := range p.Decls { if pd == d || reflect.DeepEqual(pd, d) { return true } + + n2, ok2 := pd.(Named) + if ok1 && ok2 && n1.GetName() == n2.GetName() { + return true + } } return false @@ -277,7 +284,7 @@ type AliasType struct { } func (p AliasType) Generate(out *Emitter) error { - out.Printf("type %s = %s", p.Alias, p.Name) + out.Printlnf("type %s = %s", p.Alias, p.Name) return nil } diff --git a/pkg/generator/config.go b/pkg/generator/config.go index 42bd1f76..e320da14 100644 --- a/pkg/generator/config.go +++ b/pkg/generator/config.go @@ -40,6 +40,10 @@ type Config struct { // DisableCustomTypesForMaps configures the generator to avoid creating a custom type for maps, // and to use the map type directly. DisableCustomTypesForMaps bool + // AliasSingleAllOfAnyOfRefs will convert types with a single nested anyOf or allOf ref type into a type alias. + AliasSingleAllOfAnyOfRefs bool + // PreferOmitzero will use omit omitzero instead of omitempty, note this requires Go 1.24 + PreferOmitzero bool } type SchemaMapping struct { diff --git a/pkg/generator/formatter.go b/pkg/generator/formatter.go index 407ed0cb..046e2ed3 100644 --- a/pkg/generator/formatter.go +++ b/pkg/generator/formatter.go @@ -6,6 +6,7 @@ import ( type formatter interface { addImport(out *codegen.File, declType *codegen.TypeDecl) + getName() string generate(output *output, declType *codegen.TypeDecl, validators []validator) func(*codegen.Emitter) error enumMarshal(declType *codegen.TypeDecl) func(*codegen.Emitter) error diff --git a/pkg/generator/generate.go b/pkg/generator/generate.go index 0ea248e4..554063a8 100644 --- a/pkg/generator/generate.go +++ b/pkg/generator/generate.go @@ -147,6 +147,14 @@ func (g *Generator) AddFile(fileName string, schema *schemas.Schema) error { return err } + if schema.ID != "" { + if _, processed := o.processedSchemas[schema.ID]; processed { + return nil + } + + o.processedSchemas[schema.ID] = true + } + return newSchemaGenerator(g, schema, fileName, o).generateRootType() } @@ -213,6 +221,7 @@ func (g *Generator) beginOutput( declsBySchema: map[*schemas.Type]*codegen.TypeDecl{}, declsByName: map[string]*codegen.TypeDecl{}, unmarshallersByTypeDecl: map[*codegen.TypeDecl]bool{}, + processedSchemas: map[string]bool{}, } g.outputs[id] = output diff --git a/pkg/generator/json_formatter.go b/pkg/generator/json_formatter.go index cd2fa128..d3f1a22f 100644 --- a/pkg/generator/json_formatter.go +++ b/pkg/generator/json_formatter.go @@ -172,3 +172,7 @@ func (jf *jsonFormatter) addImport(out *codegen.File, declType *codegen.TypeDecl } } } + +func (yf *jsonFormatter) getName() string { + return "json" +} diff --git a/pkg/generator/output.go b/pkg/generator/output.go index 9fd42cf6..8fe756cb 100644 --- a/pkg/generator/output.go +++ b/pkg/generator/output.go @@ -16,6 +16,7 @@ type output struct { declsByName map[string]*codegen.TypeDecl declsBySchema map[*schemas.Type]*codegen.TypeDecl unmarshallersByTypeDecl map[*codegen.TypeDecl]bool + processedSchemas map[string]bool warner func(string) } diff --git a/pkg/generator/schema_generator.go b/pkg/generator/schema_generator.go index 2e790e05..0e365a12 100644 --- a/pkg/generator/schema_generator.go +++ b/pkg/generator/schema_generator.go @@ -72,10 +72,17 @@ func (g *schemaGenerator) generateRootType() error { } func (g *schemaGenerator) generateReferencedType(t *schemas.Type) (codegen.Type, error) { - if schemaOutput, ok := g.outputs[g.schema.ID]; ok { - if decl, ok := schemaOutput.declsByName[t.Ref]; ok { - if decl != nil { - return decl.Type, nil + defName, fileName, err := g.extractRefNames(t) + if err != nil { + return nil, err + } + + if fileName == "" { + if schemaOutput, ok := g.outputs[g.schema.ID]; ok { + if decl, ok := schemaOutput.declsByName[defName]; ok { + if decl != nil { + return &codegen.NamedType{Decl: decl}, nil + } } } } @@ -92,11 +99,6 @@ func (g *schemaGenerator) generateReferencedType(t *schemas.Type) (codegen.Type, return codegen.EmptyInterfaceType{}, nil } - defName, fileName, err := g.extractRefNames(t) - if err != nil { - return nil, err - } - schema := g.schema sg := g @@ -374,6 +376,7 @@ func (g *schemaGenerator) generateDeclaredType(t *schemas.Type, scope nameScope) return &codegen.NamedType{Decl: &decl}, nil } +//nolint:gocyclo // todo: reduce cyclomatic complexity func (g *schemaGenerator) structFieldValidators( validators []validator, f codegen.StructField, @@ -391,12 +394,23 @@ func (g *schemaGenerator) structFieldValidators( validators = g.structFieldValidators(validators, f, v.Type, v.IsNillable()) case codegen.PrimitiveType: - if v.Type == schemas.TypeNameString { + switch { + case v.Type == schemas.TypeNameString: hasPattern := len(f.SchemaType.Pattern) != 0 - if f.SchemaType.MinLength != 0 || f.SchemaType.MaxLength != 0 || hasPattern { + if f.SchemaType.MinLength != 0 || f.SchemaType.MaxLength != 0 || hasPattern || f.SchemaType.Const != nil { // Double escape the escape characters so we don't effectively parse the escapes within the value. escapedPattern := f.SchemaType.Pattern + var constVal *string + + if f.SchemaType.Const != nil { + if s, ok := f.SchemaType.Const.(string); ok { + constVal = &s + } else { + g.warner(fmt.Sprintf("Ignoring non string const value: %v", f.SchemaType.Const)) + } + } + replaceJSONCharactersBy := []string{"\\b", "\\f", "\\n", "\\r", "\\t"} replaceJSONCharacters := []string{"\b", "\f", "\n", "\r", "\t"} @@ -411,6 +425,7 @@ func (g *schemaGenerator) structFieldValidators( minLength: f.SchemaType.MinLength, maxLength: f.SchemaType.MaxLength, pattern: escapedPattern, + constVal: constVal, isNillable: isNillable, }) } @@ -418,12 +433,14 @@ func (g *schemaGenerator) structFieldValidators( if hasPattern { g.output.file.Package.AddImport("regexp", "") } - } else if strings.Contains(v.Type, "int") || v.Type == float64Type { + + case strings.Contains(v.Type, "int") || v.Type == float64Type: if f.SchemaType.MultipleOf != nil || f.SchemaType.Maximum != nil || f.SchemaType.ExclusiveMaximum != nil || f.SchemaType.Minimum != nil || - f.SchemaType.ExclusiveMinimum != nil { + f.SchemaType.ExclusiveMinimum != nil || + f.SchemaType.Const != nil { validators = append(validators, &numericValidator{ jsonName: f.JSONName, fieldName: f.Name, @@ -433,6 +450,7 @@ func (g *schemaGenerator) structFieldValidators( exclusiveMaximum: f.SchemaType.ExclusiveMaximum, minimum: f.SchemaType.Minimum, exclusiveMinimum: f.SchemaType.ExclusiveMinimum, + constVal: f.SchemaType.Const, roundToInt: strings.Contains(v.Type, "int"), }) } @@ -440,6 +458,26 @@ func (g *schemaGenerator) structFieldValidators( if f.SchemaType.MultipleOf != nil && v.Type == float64Type { g.output.file.Package.AddImport("math", "") } + + case v.Type == "bool": + if f.SchemaType.Const != nil { + var constVal *bool + + if f.SchemaType.Const != nil { + if b, ok := f.SchemaType.Const.(bool); ok { + constVal = &b + } else { + g.warner(fmt.Sprintf("Ignoring non boolean const value: %v", f.SchemaType.Const)) + } + } + + validators = append(validators, &booleanValidator{ + jsonName: f.JSONName, + fieldName: f.Name, + isNillable: isNillable, + constVal: constVal, + }) + } } case *codegen.ArrayType: @@ -505,7 +543,7 @@ func (g *schemaGenerator) generateUnmarshaler(decl *codegen.TypeDecl, validators g.output.file.Package.AddDecl(&codegen.Method{ Impl: formatter.generate(g.output, decl, validators), - Name: decl.GetName() + "_validator", + Name: decl.GetName() + "_validator_" + formatter.getName(), }) } } @@ -689,11 +727,11 @@ func (g *schemaGenerator) generateStructType(t *schemas.Type, scope nameScope) ( } if len(t.AnyOf) > 0 { - return g.generateAnyOfType(t.AnyOf, scope) + return g.generateAnyOfType(t, scope) } if len(t.AllOf) > 0 { - return g.generateAllOfType(t.AllOf, scope) + return g.generateAllOfType(t, scope) } // Checking .Not here because `false` is unmarshalled to .Not = Type{}. @@ -812,11 +850,16 @@ func (g *schemaGenerator) addStructField( tags := "" - if isRequired || g.DisableOmitempty() { + switch { + case isRequired || g.DisableOmitempty(): for _, tag := range g.config.Tags { tags += fmt.Sprintf(`%s:"%s" `, tag, name) } - } else { + case g.config.PreferOmitzero: + for _, tag := range g.config.Tags { + tags += fmt.Sprintf(`%s:"%s,omitzero" `, tag, name) + } + default: for _, tag := range g.config.Tags { tags += fmt.Sprintf(`%s:"%s,omitempty" `, tag, name) } @@ -853,15 +896,32 @@ func (g *schemaGenerator) addStructField( return nil } -func (g *schemaGenerator) generateAnyOfType(anyOf []*schemas.Type, scope nameScope) (codegen.Type, error) { - if len(anyOf) == 0 { +func (g *schemaGenerator) generateAnyOfType(t *schemas.Type, scope nameScope) (codegen.Type, error) { + if len(t.AnyOf) == 0 { return nil, errEmptyInAnyOf } + if g.config.AliasSingleAllOfAnyOfRefs && len(t.AnyOf) == 1 && t.IsEmptyObject() { + childType := t.AnyOf[0] + if childType.Ref != "" { + resolvedType, err := g.resolveRef(childType) + if err == nil { + return g.generateTypeInline(resolvedType, scope) + } else { + g.warner(fmt.Sprintf("Could not resolve ref %q: %v", childType.Ref, err)) + } + } + } + isCycle := false - rAnyOf, hasNull := g.resolveRefs(anyOf, false) + rAnyOf, hasNull := g.resolveRefs(t.AnyOf, false) for i, typ := range rAnyOf { + // infer type from base if not set + if len(typ.Type) == 0 { + typ.Type = append(schemas.TypeList{}, t.Type...) + } + typ.SetSubSchemaTypeElem() ic, cleanupCycle, cycleErr := g.detectCycle(typ) @@ -890,22 +950,38 @@ func (g *schemaGenerator) generateAnyOfType(anyOf []*schemas.Type, scope nameSco return codegen.EmptyInterfaceType{}, nil } - anyOfType, err := schemas.AnyOf(rAnyOf) + anyOfType, err := schemas.AnyOf(rAnyOf, t) if err != nil { return nil, fmt.Errorf("could not merge anyOf types: %w", err) } + anyOfType.AnyOf = nil + return g.generateTypeInline(anyOfType, scope) } -func (g *schemaGenerator) generateAllOfType(allOf []*schemas.Type, scope nameScope) (codegen.Type, error) { - rAllOf, _ := g.resolveRefs(allOf, true) +func (g *schemaGenerator) generateAllOfType(t *schemas.Type, scope nameScope) (codegen.Type, error) { + if g.config.AliasSingleAllOfAnyOfRefs && len(t.AllOf) == 1 && t.IsEmptyObject() { + subType := t.AllOf[0] + if subType.Ref != "" { + resolvedType, err := g.resolveRef(subType) + if err == nil { + return g.generateTypeInline(resolvedType, scope) + } else { + g.warner(fmt.Sprintf("Could not resolve subtype ref %q: %v", subType.Ref, err)) + } + } + } - allOfType, err := schemas.AllOf(rAllOf) + rAllOf, _ := g.resolveRefs(t.AllOf, true) + + allOfType, err := schemas.AllOf(rAllOf, t) if err != nil { return nil, fmt.Errorf("could not merge allOf types: %w", err) } + allOfType.AllOf = nil + return g.generateTypeInline(allOfType, scope) } @@ -960,11 +1036,11 @@ func (g *schemaGenerator) generateTypeInline(t *schemas.Type, scope nameScope) ( } if len(t.AnyOf) > 0 { - return g.generateAnyOfType(t.AnyOf, scope) + return g.generateAnyOfType(t, scope) } if len(t.AllOf) > 0 { - return g.generateAllOfType(t.AllOf, scope) + return g.generateAllOfType(t, scope) } if len(t.Type) == 2 && typeIsNullable { @@ -1192,13 +1268,13 @@ func (g *schemaGenerator) generateEnumType( if wrapInStruct { g.output.file.Package.AddDecl(&codegen.Method{ Impl: formatter.enumMarshal(&enumDecl), - Name: enumDecl.GetName() + "_enum", + Name: enumDecl.GetName() + "_enum_" + formatter.getName(), }) } g.output.file.Package.AddDecl(&codegen.Method{ Impl: formatter.enumUnmarshal(enumDecl, enumType, valueConstant, wrapInStruct), - Name: enumDecl.GetName() + "_enum_unmarshal", + Name: enumDecl.GetName() + "_enum_unmarshal_" + formatter.getName(), }) } } @@ -1263,6 +1339,20 @@ func (g *schemaGenerator) resolveRef(t *schemas.Type) (*schemas.Type, error) { return nil, fmt.Errorf("%w: %w", errCannotResolveRef, err) } + // After resolving the ref type we lose info about the original schema + // so rewrite all nested refs to include the original schema id + _, fileName, err := g.extractRefNames(t) + if err != nil { + return nil, fmt.Errorf("%w: %w", errCannotResolveRef, err) + } + + if fileName != "" { + err = ntyp.Decl.SchemaType.ConvertAllRefs(fileName) + if err != nil { + return nil, fmt.Errorf("convert refs: %w", err) + } + } + ntyp.Decl.SchemaType.Dereferenced = true g.schemaTypesByRef[t.Ref] = ntyp.Decl.SchemaType diff --git a/pkg/generator/validator.go b/pkg/generator/validator.go index 68602358..2819311b 100644 --- a/pkg/generator/validator.go +++ b/pkg/generator/validator.go @@ -356,6 +356,7 @@ type stringValidator struct { maxLength int isNillable bool pattern string + constVal *string } func (v *stringValidator) generate(out *codegen.Emitter, format string) error { @@ -393,6 +394,14 @@ func (v *stringValidator) generate(out *codegen.Emitter, format string) error { } } + if v.constVal != nil { + out.Printlnf(`if %s%s%s != "%s" {`, checkPointer, pointerPrefix, value, *v.constVal) + out.Indent(1) + out.Printlnf(`return fmt.Errorf("field %%s: must be equal to %%s", "%s", "%s")`, fieldName, *v.constVal) + out.Indent(-1) + out.Printlnf("}") + } + if v.minLength == 0 && v.maxLength == 0 { return nil } @@ -432,6 +441,7 @@ type numericValidator struct { exclusiveMaximum *any minimum *float64 exclusiveMinimum *any + constVal any roundToInt bool } @@ -445,6 +455,14 @@ func (v *numericValidator) generate(out *codegen.Emitter, format string) error { pointerPrefix = "*" } + if v.constVal != nil { + out.Printlnf(`if %s%s%s != %v {`, checkPointer, pointerPrefix, value, v.constVal) + out.Indent(1) + out.Printlnf(`return fmt.Errorf("field %%s: must be equal to %%v", "%s", %v)`, v.jsonName, v.constVal) + out.Indent(-1) + out.Printlnf("}") + } + if v.multipleOf != nil { if v.roundToInt { out.Printlnf(`if %s %s%s %% %v != 0 {`, checkPointer, pointerPrefix, value, v.valueOf(*v.multipleOf)) @@ -527,6 +545,42 @@ func (v *numericValidator) valueOf(val float64) any { return val } +type booleanValidator struct { + jsonName string + fieldName string + isNillable bool + constVal *bool +} + +func (v *booleanValidator) generate(out *codegen.Emitter, unmarshalTemplate string) error { + value := getPlainName(v.fieldName) + fieldName := v.jsonName + checkPointer := "" + pointerPrefix := "" + + if v.isNillable { + checkPointer = fmt.Sprintf("%s != nil && ", value) + pointerPrefix = "*" + } + + if v.constVal != nil { + out.Printlnf(`if %s%s%s != %t {`, checkPointer, pointerPrefix, value, *v.constVal) + out.Indent(1) + out.Printlnf(`return fmt.Errorf("field %%s: must be equal to %%t", "%s", %t)`, fieldName, *v.constVal) + out.Indent(-1) + out.Printlnf("}") + } + + return nil +} + +func (v *booleanValidator) desc() *validatorDesc { + return &validatorDesc{ + hasError: true, + beforeJSONUnmarshal: false, + } +} + func getPlainName(fieldName string) string { if fieldName == "" { return varNamePlainStruct diff --git a/pkg/generator/yaml_formatter.go b/pkg/generator/yaml_formatter.go index 13cdd731..9dff0ed7 100644 --- a/pkg/generator/yaml_formatter.go +++ b/pkg/generator/yaml_formatter.go @@ -169,3 +169,7 @@ func (yf *yamlFormatter) addImport(out *codegen.File, declType *codegen.TypeDecl } } } + +func (yf *yamlFormatter) getName() string { + return "yaml" +} diff --git a/pkg/schemas/model.go b/pkg/schemas/model.go index fde756fa..b74fe95f 100644 --- a/pkg/schemas/model.go +++ b/pkg/schemas/model.go @@ -28,6 +28,7 @@ import ( "fmt" "reflect" "slices" + "strings" "dario.cat/mergo" ) @@ -176,6 +177,7 @@ type Type struct { AdditionalProperties *Type `json:"additionalProperties,omitempty"` // Section 5.18. Enum []interface{} `json:"enum,omitempty"` // Section 5.20. Type TypeList `json:"type,omitempty"` // Section 5.21. + Const interface{} `json:"const,omitempty"` // RFC draft-bhutton-json-schema-01, section 10. AllOf []*Type `json:"allOf,omitempty"` // Section 10.2.1.1. AnyOf []*Type `json:"anyOf,omitempty"` // Section 10.2.1.2. @@ -214,6 +216,10 @@ type Type struct { Dereferenced bool `json:"-"` // Marks that his type has been dereferenced. } +func (value *Type) IsEmptyObject() bool { + return len(value.Properties) == 0 && value.AdditionalProperties == nil +} + func (value *Type) SetSubSchemaType(sst SubSchemaType) { value.subSchemaType = sst } @@ -238,6 +244,12 @@ func (value *Type) SetSubSchemaTypeElem() { value.subSchemaTypeElem = true } +func (value *Type) ConvertAllRefs(absolutePath string) error { + val := reflect.ValueOf(value).Elem() + + return updateAllRefsValues(&val, absolutePath) +} + // UnmarshalJSON accepts booleans as schemas where `true` is equivalent to `{}` // and `false` is equivalent to `{"not": {}}`. func (value *Type) UnmarshalJSON(raw []byte) error { @@ -275,13 +287,17 @@ func (value *Type) UnmarshalJSON(raw []byte) error { obj.DependentSchemas = legacyObj.Dependencies } + if len(obj.Type) == 0 && (len(obj.Properties) > 0 || obj.AdditionalProperties != nil) { + obj.Type = TypeList{"object"} + } + *value = Type(obj) return nil } -func AllOf(types []*Type) (*Type, error) { - typ, err := MergeTypes(types) +func AllOf(types []*Type, baseType *Type) (*Type, error) { + typ, err := MergeTypes(types, baseType) if err != nil { return nil, err } @@ -291,26 +307,27 @@ func AllOf(types []*Type) (*Type, error) { return typ, nil } -func AnyOf(types []*Type) (*Type, error) { - typ, err := MergeTypes(types) +func AnyOf(types []*Type, baseType *Type) (*Type, error) { + typ, err := MergeTypes(types, baseType) if err != nil { return nil, err } + typ.Required = mergeRequiredUnion(types, baseType) typ.subSchemaType = SubSchemaTypeAnyOf typ.subSchemasCount = len(types) return typ, nil } -func MergeTypes(types []*Type) (*Type, error) { +func MergeTypes(types []*Type, baseType *Type) (*Type, error) { if len(types) == 0 { return nil, ErrEmptyTypesList } result := &Type{} - if isPrimitiveTypeList(types) { + if isPrimitiveTypeList(types, result.Type) { return result, nil } @@ -319,6 +336,10 @@ func MergeTypes(types []*Type) (*Type, error) { mergo.WithTransformers(typeListTransformer{}), } + if err := mergo.Merge(result, baseType, opts...); err != nil { + return nil, fmt.Errorf("%w: %w", ErrCannotMergeTypes, err) + } + for _, t := range types { if err := mergo.Merge(result, t, opts...); err != nil { return nil, fmt.Errorf("%w: %w", ErrCannotMergeTypes, err) @@ -328,6 +349,80 @@ func MergeTypes(types []*Type) (*Type, error) { return result, nil } +func mergeRequiredUnion(types []*Type, baseType *Type) []string { + required := make([]string, len(baseType.Required)) + copy(required, baseType.Required) + + for _, r := range types[0].Required { + valid := true + + for _, t := range types { + if !slices.Contains(t.Required, r) { + valid = false + + break + } + } + + if valid && !slices.Contains(required, r) { + required = append(required, r) //nolint:makezero + } + } + + return required +} + +func updateAllRefsValues(structValue *reflect.Value, refPath string) error { + switch structValue.Kind() { //nolint:exhaustive + case reflect.Struct: + for i := range structValue.NumField() { + field := structValue.Field(i) + name := structValue.Type().Field(i).Name + + switch field.Kind() { //nolint:exhaustive + case reflect.String: + fieldVal := field.String() + if name == "Ref" && fieldVal != "" && field.CanSet() { + if strings.HasPrefix(fieldVal, "#") { + field.SetString(refPath + fieldVal) + } + } + + default: + if err := updateAllRefsValues(&field, refPath); err != nil { + return fmt.Errorf("struct error: %w", err) + } + } + } + + case reflect.Ptr: + elem := structValue.Elem() + if !structValue.IsNil() { + if err := updateAllRefsValues(&elem, refPath); err != nil { + return fmt.Errorf("ptr error: %w", err) + } + } + + case reflect.Map: + for _, key := range structValue.MapKeys() { + val := structValue.MapIndex(key) + if err := updateAllRefsValues(&val, refPath); err != nil { + return fmt.Errorf("map error: %w", err) + } + } + + case reflect.Slice, reflect.Array: + for i := range structValue.Len() { + field := structValue.Index(i) + if err := updateAllRefsValues(&field, refPath); err != nil { + return fmt.Errorf("slice error: %w", err) + } + } + } + + return nil +} + type typeListTransformer struct{} func (t typeListTransformer) Transformer(typ reflect.Type) func(dst, src reflect.Value) error { diff --git a/pkg/schemas/types.go b/pkg/schemas/types.go index be224f21..8d55e7a6 100644 --- a/pkg/schemas/types.go +++ b/pkg/schemas/types.go @@ -27,11 +27,18 @@ func CleanNameForSorting(name string) string { if strings.HasPrefix(name, PrefixEnumValue) { return strings.TrimPrefix(name, PrefixEnumValue) + "_enumValues" // Append a string for sorting properly. } + // Preserve existing sort order in tests + name = strings.TrimSuffix(name, "_yaml") + name = strings.TrimSuffix(name, "_json") return name } -func isPrimitiveTypeList(types []*Type) bool { +func isPrimitiveTypeList(types []*Type, baseType TypeList) bool { + if len(baseType) > 0 && !IsPrimitiveType(baseType[0]) { + return false + } + for _, typ := range types { if len(typ.Type) == 0 { continue diff --git a/tests/data/aliasSingleAllOfAnyOfRefs/allOf/allOf.go b/tests/data/aliasSingleAllOfAnyOfRefs/allOf/allOf.go new file mode 100644 index 00000000..0c069999 --- /dev/null +++ b/tests/data/aliasSingleAllOfAnyOfRefs/allOf/allOf.go @@ -0,0 +1,37 @@ +// Code generated by github.com/atombender/go-jsonschema, DO NOT EDIT. + +package test + +import "encoding/json" +import yaml "gopkg.in/yaml.v3" + +type Thing struct { + // Values corresponds to the JSON schema field "values". + Values []Value `json:"values,omitempty" yaml:"values,omitempty" mapstructure:"values,omitempty"` +} + +// UnmarshalJSON implements json.Unmarshaler. +func (j *Thing) UnmarshalJSON(value []byte) error { + type Plain Thing + var plain Plain + if err := json.Unmarshal(value, &plain); err != nil { + return err + } + *j = Thing(plain) + return nil +} + +// UnmarshalYAML implements yaml.Unmarshaler. +func (j *Thing) UnmarshalYAML(value *yaml.Node) error { + type Plain Thing + var plain Plain + if err := value.Decode(&plain); err != nil { + return err + } + *j = Thing(plain) + return nil +} + +type Value float64 + +type AllOf = Thing diff --git a/tests/data/aliasSingleAllOfAnyOfRefs/allOf/allOf.json b/tests/data/aliasSingleAllOfAnyOfRefs/allOf/allOf.json new file mode 100644 index 00000000..d86e4f28 --- /dev/null +++ b/tests/data/aliasSingleAllOfAnyOfRefs/allOf/allOf.json @@ -0,0 +1,28 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "type": "object", + "allOf": [ + { + "$ref": "#/definitions/Thing" + } + ], + "definitions": { + "Thing": { + "allOf": [ + { + "properties": { + "values": { + "type": "array", + "items": { + "$ref": "#/definitions/Value" + } + } + } + } + ] + }, + "Value": { + "type": "number" + } + } +} diff --git a/tests/data/aliasSingleAllOfAnyOfRefs/anyOf/anyOf.go b/tests/data/aliasSingleAllOfAnyOfRefs/anyOf/anyOf.go new file mode 100644 index 00000000..3e7ce60a --- /dev/null +++ b/tests/data/aliasSingleAllOfAnyOfRefs/anyOf/anyOf.go @@ -0,0 +1,90 @@ +// Code generated by github.com/atombender/go-jsonschema, DO NOT EDIT. + +package test + +import "encoding/json" +import "errors" +import "fmt" +import yaml "gopkg.in/yaml.v3" + +type Thing struct { + // Values corresponds to the JSON schema field "values". + Values []Value `json:"values,omitempty" yaml:"values,omitempty" mapstructure:"values,omitempty"` +} + +type Thing_0 struct { + // Values corresponds to the JSON schema field "values". + Values []Value `json:"values,omitempty" yaml:"values,omitempty" mapstructure:"values,omitempty"` +} + +// UnmarshalJSON implements json.Unmarshaler. +func (j *Thing_0) UnmarshalJSON(value []byte) error { + type Plain Thing_0 + var plain Plain + if err := json.Unmarshal(value, &plain); err != nil { + return err + } + *j = Thing_0(plain) + return nil +} + +// UnmarshalYAML implements yaml.Unmarshaler. +func (j *Thing_0) UnmarshalYAML(value *yaml.Node) error { + type Plain Thing_0 + var plain Plain + if err := value.Decode(&plain); err != nil { + return err + } + *j = Thing_0(plain) + return nil +} + +// UnmarshalJSON implements json.Unmarshaler. +func (j *Thing) UnmarshalJSON(value []byte) error { + var raw map[string]interface{} + if err := json.Unmarshal(value, &raw); err != nil { + return err + } + var thing_0 Thing_0 + var errs []error + if err := thing_0.UnmarshalJSON(value); err != nil { + errs = append(errs, err) + } + if len(errs) == 1 { + return fmt.Errorf("all validators failed: %s", errors.Join(errs...)) + } + type Plain Thing + var plain Plain + if err := json.Unmarshal(value, &plain); err != nil { + return err + } + *j = Thing(plain) + return nil +} + +// UnmarshalYAML implements yaml.Unmarshaler. +func (j *Thing) UnmarshalYAML(value *yaml.Node) error { + var raw map[string]interface{} + if err := value.Decode(&raw); err != nil { + return err + } + var thing_0 Thing_0 + var errs []error + if err := thing_0.UnmarshalYAML(value); err != nil { + errs = append(errs, err) + } + if len(errs) == 1 { + return fmt.Errorf("all validators failed: %s", errors.Join(errs...)) + } + type Plain Thing + var plain Plain + if err := value.Decode(&plain); err != nil { + return err + } + *j = Thing(plain) + return nil +} + +type Value float64 + +type AnyOf = Thing diff --git a/tests/data/aliasSingleAllOfAnyOfRefs/anyOf/anyOf.json b/tests/data/aliasSingleAllOfAnyOfRefs/anyOf/anyOf.json new file mode 100644 index 00000000..ea93faae --- /dev/null +++ b/tests/data/aliasSingleAllOfAnyOfRefs/anyOf/anyOf.json @@ -0,0 +1,28 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "type": "object", + "anyOf": [ + { + "$ref": "#/definitions/Thing" + } + ], + "definitions": { + "Thing": { + "anyOf": [ + { + "properties": { + "values": { + "type": "array", + "items": { + "$ref": "#/definitions/Value" + } + } + } + } + ] + }, + "Value": { + "type": "number" + } + } +} diff --git a/tests/data/aliasSingleAllOfAnyOfRefs/externalRef/externalRef.go b/tests/data/aliasSingleAllOfAnyOfRefs/externalRef/externalRef.go new file mode 100644 index 00000000..ca7bb16e --- /dev/null +++ b/tests/data/aliasSingleAllOfAnyOfRefs/externalRef/externalRef.go @@ -0,0 +1,39 @@ +// Code generated by github.com/atombender/go-jsonschema, DO NOT EDIT. + +package test + +import "encoding/json" +import yaml "gopkg.in/yaml.v3" + +type Thing struct { + // Values corresponds to the JSON schema field "values". + Values []Value `json:"values,omitempty" yaml:"values,omitempty" mapstructure:"values,omitempty"` +} + +// UnmarshalJSON implements json.Unmarshaler. +func (j *Thing) UnmarshalJSON(value []byte) error { + type Plain Thing + var plain Plain + if err := json.Unmarshal(value, &plain); err != nil { + return err + } + *j = Thing(plain) + return nil +} + +// UnmarshalYAML implements yaml.Unmarshaler. +func (j *Thing) UnmarshalYAML(value *yaml.Node) error { + type Plain Thing + var plain Plain + if err := value.Decode(&plain); err != nil { + return err + } + *j = Thing(plain) + return nil +} + +type Value float64 + +type AllOf = Thing + +type ExternalRef = Thing diff --git a/tests/data/aliasSingleAllOfAnyOfRefs/externalRef/externalRef.json b/tests/data/aliasSingleAllOfAnyOfRefs/externalRef/externalRef.json new file mode 100644 index 00000000..69b92d0a --- /dev/null +++ b/tests/data/aliasSingleAllOfAnyOfRefs/externalRef/externalRef.json @@ -0,0 +1,10 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "id": "https://example.com/refExternalFileAllOfNestedRefs", + "type": "object", + "allOf": [ + { + "$ref": "../allOf/allOf.json#/$defs/Thing" + } + ] +} diff --git a/tests/data/core/allOf/allOfMultipleRequired.go b/tests/data/core/allOf/allOfMultipleRequired.go new file mode 100644 index 00000000..ed62dd2e --- /dev/null +++ b/tests/data/core/allOf/allOfMultipleRequired.go @@ -0,0 +1,169 @@ +// Code generated by github.com/atombender/go-jsonschema, DO NOT EDIT. + +package test + +import "encoding/json" +import "fmt" +import "github.com/go-viper/mapstructure/v2" +import yaml "gopkg.in/yaml.v3" +import "reflect" +import "strings" + +type ComposedWithMultipleRequired struct { + // BaseField corresponds to the JSON schema field "baseField". + BaseField string `json:"baseField" yaml:"baseField" mapstructure:"baseField"` + + // DirectField corresponds to the JSON schema field "directField". + DirectField bool `json:"directField" yaml:"directField" mapstructure:"directField"` + + // MiddleField corresponds to the JSON schema field "middleField". + MiddleField float64 `json:"middleField" yaml:"middleField" mapstructure:"middleField"` + + AdditionalProperties interface{} `mapstructure:",remain"` +} + +// UnmarshalJSON implements json.Unmarshaler. +func (j *ComposedWithMultipleRequired) UnmarshalJSON(value []byte) error { + var raw map[string]interface{} + if err := json.Unmarshal(value, &raw); err != nil { + return err + } + if _, ok := raw["baseField"]; raw != nil && !ok { + return fmt.Errorf("field baseField in ComposedWithMultipleRequired: required") + } + if _, ok := raw["directField"]; raw != nil && !ok { + return fmt.Errorf("field directField in ComposedWithMultipleRequired: required") + } + if _, ok := raw["middleField"]; raw != nil && !ok { + return fmt.Errorf("field middleField in ComposedWithMultipleRequired: required") + } + type Plain ComposedWithMultipleRequired + var plain Plain + if err := json.Unmarshal(value, &plain); err != nil { + return err + } + st := reflect.TypeOf(Plain{}) + for i := range st.NumField() { + delete(raw, st.Field(i).Name) + delete(raw, strings.Split(st.Field(i).Tag.Get("json"), ",")[0]) + } + if err := mapstructure.Decode(raw, &plain.AdditionalProperties); err != nil { + return err + } + *j = ComposedWithMultipleRequired(plain) + return nil +} + +// UnmarshalYAML implements yaml.Unmarshaler. +func (j *ComposedWithMultipleRequired) UnmarshalYAML(value *yaml.Node) error { + var raw map[string]interface{} + if err := value.Decode(&raw); err != nil { + return err + } + if _, ok := raw["baseField"]; raw != nil && !ok { + return fmt.Errorf("field baseField in ComposedWithMultipleRequired: required") + } + if _, ok := raw["directField"]; raw != nil && !ok { + return fmt.Errorf("field directField in ComposedWithMultipleRequired: required") + } + if _, ok := raw["middleField"]; raw != nil && !ok { + return fmt.Errorf("field middleField in ComposedWithMultipleRequired: required") + } + type Plain ComposedWithMultipleRequired + var plain Plain + if err := value.Decode(&plain); err != nil { + return err + } + st := reflect.TypeOf(Plain{}) + for i := range st.NumField() { + delete(raw, st.Field(i).Name) + delete(raw, strings.Split(st.Field(i).Tag.Get("json"), ",")[0]) + } + if err := mapstructure.Decode(raw, &plain.AdditionalProperties); err != nil { + return err + } + *j = ComposedWithMultipleRequired(plain) + return nil +} + +type MultipleRequiredBase struct { + // BaseField corresponds to the JSON schema field "baseField". + BaseField string `json:"baseField" yaml:"baseField" mapstructure:"baseField"` +} + +// UnmarshalJSON implements json.Unmarshaler. +func (j *MultipleRequiredBase) UnmarshalJSON(value []byte) error { + var raw map[string]interface{} + if err := json.Unmarshal(value, &raw); err != nil { + return err + } + if _, ok := raw["baseField"]; raw != nil && !ok { + return fmt.Errorf("field baseField in MultipleRequiredBase: required") + } + type Plain MultipleRequiredBase + var plain Plain + if err := json.Unmarshal(value, &plain); err != nil { + return err + } + *j = MultipleRequiredBase(plain) + return nil +} + +// UnmarshalYAML implements yaml.Unmarshaler. +func (j *MultipleRequiredBase) UnmarshalYAML(value *yaml.Node) error { + var raw map[string]interface{} + if err := value.Decode(&raw); err != nil { + return err + } + if _, ok := raw["baseField"]; raw != nil && !ok { + return fmt.Errorf("field baseField in MultipleRequiredBase: required") + } + type Plain MultipleRequiredBase + var plain Plain + if err := value.Decode(&plain); err != nil { + return err + } + *j = MultipleRequiredBase(plain) + return nil +} + +type MultipleRequiredMiddle struct { + // MiddleField corresponds to the JSON schema field "middleField". + MiddleField float64 `json:"middleField" yaml:"middleField" mapstructure:"middleField"` +} + +// UnmarshalJSON implements json.Unmarshaler. +func (j *MultipleRequiredMiddle) UnmarshalJSON(value []byte) error { + var raw map[string]interface{} + if err := json.Unmarshal(value, &raw); err != nil { + return err + } + if _, ok := raw["middleField"]; raw != nil && !ok { + return fmt.Errorf("field middleField in MultipleRequiredMiddle: required") + } + type Plain MultipleRequiredMiddle + var plain Plain + if err := json.Unmarshal(value, &plain); err != nil { + return err + } + *j = MultipleRequiredMiddle(plain) + return nil +} + +// UnmarshalYAML implements yaml.Unmarshaler. +func (j *MultipleRequiredMiddle) UnmarshalYAML(value *yaml.Node) error { + var raw map[string]interface{} + if err := value.Decode(&raw); err != nil { + return err + } + if _, ok := raw["middleField"]; raw != nil && !ok { + return fmt.Errorf("field middleField in MultipleRequiredMiddle: required") + } + type Plain MultipleRequiredMiddle + var plain Plain + if err := value.Decode(&plain); err != nil { + return err + } + *j = MultipleRequiredMiddle(plain) + return nil +} diff --git a/tests/data/core/allOf/allOfMultipleRequired.json b/tests/data/core/allOf/allOfMultipleRequired.json new file mode 100644 index 00000000..f0df11ed --- /dev/null +++ b/tests/data/core/allOf/allOfMultipleRequired.json @@ -0,0 +1,41 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "definitions": { + "MultipleRequiredBase": { + "type": "object", + "properties": { + "baseField": { + "type": "string" + } + }, + "required": ["baseField"] + }, + "MultipleRequiredMiddle": { + "type": "object", + "properties": { + "middleField": { + "type": "number" + } + }, + "required": ["middleField"] + }, + "ComposedWithMultipleRequired": { + "allOf": [ + { + "$ref": "#/definitions/MultipleRequiredBase" + }, + { + "$ref": "#/definitions/MultipleRequiredMiddle" + } + ], + "type": "object", + "additionalProperties": true, + "properties": { + "directField": { + "type": "boolean" + } + }, + "required": ["directField"] + } + } +} diff --git a/tests/data/core/allOf/allOfNestedRefs.go b/tests/data/core/allOf/allOfNestedRefs.go new file mode 100644 index 00000000..68bc24f7 --- /dev/null +++ b/tests/data/core/allOf/allOfNestedRefs.go @@ -0,0 +1,100 @@ +// Code generated by github.com/atombender/go-jsonschema, DO NOT EDIT. + +package test + +import "encoding/json" +import "fmt" +import yaml "gopkg.in/yaml.v3" + +type AllOfNestedRefs struct { + // Bar corresponds to the JSON schema field "bar". + Bar *string `json:"bar,omitempty" yaml:"bar,omitempty" mapstructure:"bar,omitempty"` + + // Foo corresponds to the JSON schema field "foo". + Foo interface{} `json:"foo" yaml:"foo" mapstructure:"foo"` +} + +// UnmarshalJSON implements json.Unmarshaler. +func (j *AllOfNestedRefs) UnmarshalJSON(value []byte) error { + var raw map[string]interface{} + if err := json.Unmarshal(value, &raw); err != nil { + return err + } + if _, ok := raw["foo"]; raw != nil && !ok { + return fmt.Errorf("field foo in AllOfNestedRefs: required") + } + type Plain AllOfNestedRefs + var plain Plain + if err := json.Unmarshal(value, &plain); err != nil { + return err + } + *j = AllOfNestedRefs(plain) + return nil +} + +// UnmarshalYAML implements yaml.Unmarshaler. +func (j *AllOfNestedRefs) UnmarshalYAML(value *yaml.Node) error { + var raw map[string]interface{} + if err := value.Decode(&raw); err != nil { + return err + } + if _, ok := raw["foo"]; raw != nil && !ok { + return fmt.Errorf("field foo in AllOfNestedRefs: required") + } + type Plain AllOfNestedRefs + var plain Plain + if err := value.Decode(&plain); err != nil { + return err + } + *j = AllOfNestedRefs(plain) + return nil +} + +type ExtraProps struct { + // Bar corresponds to the JSON schema field "bar". + Bar *string `json:"bar,omitempty" yaml:"bar,omitempty" mapstructure:"bar,omitempty"` +} + +type RootObject struct { + // Bar corresponds to the JSON schema field "bar". + Bar *string `json:"bar,omitempty" yaml:"bar,omitempty" mapstructure:"bar,omitempty"` + + // Foo corresponds to the JSON schema field "foo". + Foo interface{} `json:"foo" yaml:"foo" mapstructure:"foo"` +} + +// UnmarshalJSON implements json.Unmarshaler. +func (j *RootObject) UnmarshalJSON(value []byte) error { + var raw map[string]interface{} + if err := json.Unmarshal(value, &raw); err != nil { + return err + } + if _, ok := raw["foo"]; raw != nil && !ok { + return fmt.Errorf("field foo in RootObject: required") + } + type Plain RootObject + var plain Plain + if err := json.Unmarshal(value, &plain); err != nil { + return err + } + *j = RootObject(plain) + return nil +} + +// UnmarshalYAML implements yaml.Unmarshaler. +func (j *RootObject) UnmarshalYAML(value *yaml.Node) error { + var raw map[string]interface{} + if err := value.Decode(&raw); err != nil { + return err + } + if _, ok := raw["foo"]; raw != nil && !ok { + return fmt.Errorf("field foo in RootObject: required") + } + type Plain RootObject + var plain Plain + if err := value.Decode(&plain); err != nil { + return err + } + *j = RootObject(plain) + return nil +} diff --git a/tests/data/core/allOf/allOfNestedRefs.json b/tests/data/core/allOf/allOfNestedRefs.json new file mode 100644 index 00000000..6cd09f5a --- /dev/null +++ b/tests/data/core/allOf/allOfNestedRefs.json @@ -0,0 +1,35 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "RootSchema", + "type": "object", + "allOf": [ + { + "$ref": "#/definitions/RootObject" + } + ], + "definitions": { + "RootObject": { + "type": "object", + "allOf": [ + { + "$ref": "#/definitions/ExtraProps" + } + ], + "properties": { + "foo": { + } + }, + "required": [ + "foo" + ] + }, + "ExtraProps": { + "type": "object", + "properties": { + "bar": { + "type": "string" + } + } + } + } +} diff --git a/tests/data/core/allOf/allOfWithAnyOf.go b/tests/data/core/allOf/allOfWithAnyOf.go new file mode 100644 index 00000000..c1b7b00a --- /dev/null +++ b/tests/data/core/allOf/allOfWithAnyOf.go @@ -0,0 +1,129 @@ +// Code generated by github.com/atombender/go-jsonschema, DO NOT EDIT. + +package test + +import "encoding/json" +import "errors" +import "fmt" +import yaml "gopkg.in/yaml.v3" + +type Agreement struct { + // Type corresponds to the JSON schema field "@type". + Type string `json:"@type" yaml:"@type" mapstructure:"@type"` + + // Permission corresponds to the JSON schema field "permission". + Permission *string `json:"permission,omitempty" yaml:"permission,omitempty" mapstructure:"permission,omitempty"` + + // Prohibition corresponds to the JSON schema field "prohibition". + Prohibition *float64 `json:"prohibition,omitempty" yaml:"prohibition,omitempty" mapstructure:"prohibition,omitempty"` +} + +type Agreement_0 map[string]interface{} + +// UnmarshalJSON implements json.Unmarshaler. +func (j *Agreement_0) UnmarshalJSON(value []byte) error { + type Plain Agreement_0 + var plain Plain + if err := json.Unmarshal(value, &plain); err != nil { + return err + } + *j = Agreement_0(plain) + return nil +} + +// UnmarshalYAML implements yaml.Unmarshaler. +func (j *Agreement_0) UnmarshalYAML(value *yaml.Node) error { + type Plain Agreement_0 + var plain Plain + if err := value.Decode(&plain); err != nil { + return err + } + *j = Agreement_0(plain) + return nil +} + +type Agreement_1 map[string]interface{} + +// UnmarshalJSON implements json.Unmarshaler. +func (j *Agreement_1) UnmarshalJSON(value []byte) error { + type Plain Agreement_1 + var plain Plain + if err := json.Unmarshal(value, &plain); err != nil { + return err + } + *j = Agreement_1(plain) + return nil +} + +// UnmarshalYAML implements yaml.Unmarshaler. +func (j *Agreement_1) UnmarshalYAML(value *yaml.Node) error { + type Plain Agreement_1 + var plain Plain + if err := value.Decode(&plain); err != nil { + return err + } + *j = Agreement_1(plain) + return nil +} + +// UnmarshalJSON implements json.Unmarshaler. +func (j *Agreement) UnmarshalJSON(value []byte) error { + var raw map[string]interface{} + if err := json.Unmarshal(value, &raw); err != nil { + return err + } + var agreement_0 Agreement_0 + var agreement_1 Agreement_1 + var errs []error + if err := agreement_0.UnmarshalJSON(value); err != nil { + errs = append(errs, err) + } + if err := agreement_1.UnmarshalJSON(value); err != nil { + errs = append(errs, err) + } + if len(errs) == 2 { + return fmt.Errorf("all validators failed: %s", errors.Join(errs...)) + } + type Plain Agreement + var plain Plain + if err := json.Unmarshal(value, &plain); err != nil { + return err + } + *j = Agreement(plain) + return nil +} + +// UnmarshalYAML implements yaml.Unmarshaler. +func (j *Agreement) UnmarshalYAML(value *yaml.Node) error { + var raw map[string]interface{} + if err := value.Decode(&raw); err != nil { + return err + } + var agreement_0 Agreement_0 + var agreement_1 Agreement_1 + var errs []error + if err := agreement_0.UnmarshalYAML(value); err != nil { + errs = append(errs, err) + } + if err := agreement_1.UnmarshalYAML(value); err != nil { + errs = append(errs, err) + } + if len(errs) == 2 { + return fmt.Errorf("all validators failed: %s", errors.Join(errs...)) + } + type Plain Agreement + var plain Plain + if err := value.Decode(&plain); err != nil { + return err + } + *j = Agreement(plain) + return nil +} + +type CommonType struct { + // Permission corresponds to the JSON schema field "permission". + Permission *string `json:"permission,omitempty" yaml:"permission,omitempty" mapstructure:"permission,omitempty"` + + // Prohibition corresponds to the JSON schema field "prohibition". + Prohibition *float64 `json:"prohibition,omitempty" yaml:"prohibition,omitempty" mapstructure:"prohibition,omitempty"` +} diff --git a/tests/data/core/allOf/allOfWithAnyOf.json b/tests/data/core/allOf/allOfWithAnyOf.json new file mode 100644 index 00000000..101865db --- /dev/null +++ b/tests/data/core/allOf/allOfWithAnyOf.json @@ -0,0 +1,49 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "definitions": { + "Agreement": { + "type": "object", + "allOf": [ + { + "$ref": "#/definitions/CommonType" + }, + { + "properties": { + "@type": { + "type": "string", + "const": "Agreement" + } + } + }, + { + "anyOf": [ + { + "required": [ + "permission" + ] + }, + { + "required": [ + "prohibition" + ] + } + ] + } + ], + "required": [ + "@type" + ] + }, + "CommonType": { + "type": "object", + "properties": { + "permission": { + "type": "string" + }, + "prohibition": { + "type": "number" + } + } + } + } +} diff --git a/tests/data/core/allOf/allOfWithDirectProperties.go b/tests/data/core/allOf/allOfWithDirectProperties.go new file mode 100644 index 00000000..38fda093 --- /dev/null +++ b/tests/data/core/allOf/allOfWithDirectProperties.go @@ -0,0 +1,113 @@ +// Code generated by github.com/atombender/go-jsonschema, DO NOT EDIT. + +package test + +import "encoding/json" +import "fmt" +import "github.com/go-viper/mapstructure/v2" +import yaml "gopkg.in/yaml.v3" +import "reflect" +import "strings" + +type BaseObject struct { + // BaseField corresponds to the JSON schema field "BaseField". + BaseField string `json:"BaseField" yaml:"BaseField" mapstructure:"BaseField"` +} + +// UnmarshalJSON implements json.Unmarshaler. +func (j *BaseObject) UnmarshalJSON(value []byte) error { + var raw map[string]interface{} + if err := json.Unmarshal(value, &raw); err != nil { + return err + } + if _, ok := raw["BaseField"]; raw != nil && !ok { + return fmt.Errorf("field BaseField in BaseObject: required") + } + type Plain BaseObject + var plain Plain + if err := json.Unmarshal(value, &plain); err != nil { + return err + } + *j = BaseObject(plain) + return nil +} + +// UnmarshalYAML implements yaml.Unmarshaler. +func (j *BaseObject) UnmarshalYAML(value *yaml.Node) error { + var raw map[string]interface{} + if err := value.Decode(&raw); err != nil { + return err + } + if _, ok := raw["BaseField"]; raw != nil && !ok { + return fmt.Errorf("field BaseField in BaseObject: required") + } + type Plain BaseObject + var plain Plain + if err := value.Decode(&plain); err != nil { + return err + } + *j = BaseObject(plain) + return nil +} + +type ComposedWithAllOfAndProperties struct { + // BaseField corresponds to the JSON schema field "BaseField". + BaseField string `json:"BaseField" yaml:"BaseField" mapstructure:"BaseField"` + + // DirectField corresponds to the JSON schema field "DirectField". + DirectField []string `json:"DirectField,omitempty" yaml:"DirectField,omitempty" mapstructure:"DirectField,omitempty"` + + AdditionalProperties interface{} `mapstructure:",remain"` +} + +// UnmarshalJSON implements json.Unmarshaler. +func (j *ComposedWithAllOfAndProperties) UnmarshalJSON(value []byte) error { + var raw map[string]interface{} + if err := json.Unmarshal(value, &raw); err != nil { + return err + } + if _, ok := raw["BaseField"]; raw != nil && !ok { + return fmt.Errorf("field BaseField in ComposedWithAllOfAndProperties: required") + } + type Plain ComposedWithAllOfAndProperties + var plain Plain + if err := json.Unmarshal(value, &plain); err != nil { + return err + } + st := reflect.TypeOf(Plain{}) + for i := range st.NumField() { + delete(raw, st.Field(i).Name) + delete(raw, strings.Split(st.Field(i).Tag.Get("json"), ",")[0]) + } + if err := mapstructure.Decode(raw, &plain.AdditionalProperties); err != nil { + return err + } + *j = ComposedWithAllOfAndProperties(plain) + return nil +} + +// UnmarshalYAML implements yaml.Unmarshaler. +func (j *ComposedWithAllOfAndProperties) UnmarshalYAML(value *yaml.Node) error { + var raw map[string]interface{} + if err := value.Decode(&raw); err != nil { + return err + } + if _, ok := raw["BaseField"]; raw != nil && !ok { + return fmt.Errorf("field BaseField in ComposedWithAllOfAndProperties: required") + } + type Plain ComposedWithAllOfAndProperties + var plain Plain + if err := value.Decode(&plain); err != nil { + return err + } + st := reflect.TypeOf(Plain{}) + for i := range st.NumField() { + delete(raw, st.Field(i).Name) + delete(raw, strings.Split(st.Field(i).Tag.Get("json"), ",")[0]) + } + if err := mapstructure.Decode(raw, &plain.AdditionalProperties); err != nil { + return err + } + *j = ComposedWithAllOfAndProperties(plain) + return nil +} diff --git a/tests/data/core/allOf/allOfWithDirectProperties.json b/tests/data/core/allOf/allOfWithDirectProperties.json new file mode 100644 index 00000000..b82fb479 --- /dev/null +++ b/tests/data/core/allOf/allOfWithDirectProperties.json @@ -0,0 +1,32 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "definitions": { + "BaseObject": { + "type": "object", + "properties": { + "BaseField": { + "type": "string" + } + }, + "required": ["BaseField"] + }, + "ComposedWithAllOfAndProperties": { + "allOf": [ + { + "$ref": "#/definitions/BaseObject" + } + ], + "type": "object", + "additionalProperties": true, + "properties": { + "DirectField": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + } + } + } + } +} diff --git a/tests/data/core/allOf/issue6.go b/tests/data/core/allOf/issue6.go index 705f214a..75162bc1 100644 --- a/tests/data/core/allOf/issue6.go +++ b/tests/data/core/allOf/issue6.go @@ -6,9 +6,19 @@ import "encoding/json" import "fmt" import yaml "gopkg.in/yaml.v3" import "reflect" +import "time" // Base definition for all elements in a resource. -type Element interface{} +type Element struct { + // Additional content defined by implementations. + Extension []string `json:"extension,omitempty" yaml:"extension,omitempty" mapstructure:"extension,omitempty"` + + // Unique id for the element within a resource (for internal references). + Id *string `json:"id,omitempty" yaml:"id,omitempty" mapstructure:"id,omitempty"` + + // Name for the element + Name *string `json:"name,omitempty" yaml:"name,omitempty" mapstructure:"name,omitempty"` +} // see http://hl7.org/fhir/json.html#schema for information about the FHIR Json // Schemas @@ -20,7 +30,7 @@ type Issue6 struct { // A human's name with the ability to identify parts and usage. type Issue6Name struct { // Extensions for family - Family Element `json:"_family,omitempty" yaml:"_family,omitempty" mapstructure:"_family,omitempty"` + Family *Element `json:"_family,omitempty" yaml:"_family,omitempty" mapstructure:"_family,omitempty"` // Extensions for given Given []Element `json:"_given,omitempty" yaml:"_given,omitempty" mapstructure:"_given,omitempty"` @@ -32,10 +42,10 @@ type Issue6Name struct { Suffix []Element `json:"_suffix,omitempty" yaml:"_suffix,omitempty" mapstructure:"_suffix,omitempty"` // Extensions for text - Text Element `json:"_text,omitempty" yaml:"_text,omitempty" mapstructure:"_text,omitempty"` + Text *Element `json:"_text,omitempty" yaml:"_text,omitempty" mapstructure:"_text,omitempty"` // Extensions for use - Use Element `json:"_use,omitempty" yaml:"_use,omitempty" mapstructure:"_use,omitempty"` + Use *Element `json:"_use,omitempty" yaml:"_use,omitempty" mapstructure:"_use,omitempty"` // The part of a name that links to the genealogy. In some cultures (e.g. Eritrea) // the family name of a son is the first name of his father. @@ -45,7 +55,7 @@ type Issue6Name struct { Given_2 []string `json:"given,omitempty" yaml:"given,omitempty" mapstructure:"given,omitempty"` // Indicates the period of time when this name was valid for the named person. - Period Period `json:"period,omitempty" yaml:"period,omitempty" mapstructure:"period,omitempty"` + Period *Period `json:"period,omitempty" yaml:"period,omitempty" mapstructure:"period,omitempty"` // Part of the name that is acquired as a title due to academic, legal, employment // or nobility status, etc. and that appears at the start of the name. @@ -123,4 +133,11 @@ func (j *Issue6NameUse_2) UnmarshalJSON(value []byte) error { } // Something -type Period interface{} +type Period struct { + // The end of the period. If the end of the period is missing, it means that the + // period is ongoing. + End *time.Time `json:"end,omitempty" yaml:"end,omitempty" mapstructure:"end,omitempty"` + + // The start of the period. The boundary is inclusive. + Start *time.Time `json:"start,omitempty" yaml:"start,omitempty" mapstructure:"start,omitempty"` +} diff --git a/tests/data/core/anyOf/anyOf.1.go b/tests/data/core/anyOf/anyOf.1.go index 5584cf2e..58e05d0e 100644 --- a/tests/data/core/anyOf/anyOf.1.go +++ b/tests/data/core/anyOf/anyOf.1.go @@ -18,13 +18,13 @@ type AnyOf1 struct { type AnyOf1ConfigurationsElem struct { // Bar corresponds to the JSON schema field "bar". - Bar float64 `json:"bar" yaml:"bar" mapstructure:"bar"` + Bar *float64 `json:"bar,omitempty" yaml:"bar,omitempty" mapstructure:"bar,omitempty"` // Baz corresponds to the JSON schema field "baz". Baz *bool `json:"baz,omitempty" yaml:"baz,omitempty" mapstructure:"baz,omitempty"` // Foo corresponds to the JSON schema field "foo". - Foo string `json:"foo" yaml:"foo" mapstructure:"foo"` + Foo *string `json:"foo,omitempty" yaml:"foo,omitempty" mapstructure:"foo,omitempty"` } type AnyOf1ConfigurationsElem_0 struct { diff --git a/tests/data/core/anyOf/anyOf.2.go b/tests/data/core/anyOf/anyOf.2.go index 8a68d370..1d21546b 100644 --- a/tests/data/core/anyOf/anyOf.2.go +++ b/tests/data/core/anyOf/anyOf.2.go @@ -15,13 +15,13 @@ type AnyOf2 struct { type AnyOf2ConfigurationsElem struct { // Bar corresponds to the JSON schema field "bar". - Bar float64 `json:"bar" yaml:"bar" mapstructure:"bar"` + Bar *float64 `json:"bar,omitempty" yaml:"bar,omitempty" mapstructure:"bar,omitempty"` // Baz corresponds to the JSON schema field "baz". Baz *bool `json:"baz,omitempty" yaml:"baz,omitempty" mapstructure:"baz,omitempty"` // Foo corresponds to the JSON schema field "foo". - Foo string `json:"foo" yaml:"foo" mapstructure:"foo"` + Foo *string `json:"foo,omitempty" yaml:"foo,omitempty" mapstructure:"foo,omitempty"` } type AnyOf2ConfigurationsElem_1 struct { diff --git a/tests/data/core/anyOf/anyOf.3.go b/tests/data/core/anyOf/anyOf.3.go index a723d5c9..e9ac8b7c 100644 --- a/tests/data/core/anyOf/anyOf.3.go +++ b/tests/data/core/anyOf/anyOf.3.go @@ -7,15 +7,16 @@ import "errors" import "fmt" import yaml "gopkg.in/yaml.v3" +// object with anyOf properties as root type AnyOf3 struct { // Bar corresponds to the JSON schema field "bar". - Bar float64 `json:"bar" yaml:"bar" mapstructure:"bar"` + Bar *float64 `json:"bar,omitempty" yaml:"bar,omitempty" mapstructure:"bar,omitempty"` // Configurations corresponds to the JSON schema field "configurations". Configurations []interface{} `json:"configurations,omitempty" yaml:"configurations,omitempty" mapstructure:"configurations,omitempty"` // Foo corresponds to the JSON schema field "foo". - Foo string `json:"foo" yaml:"foo" mapstructure:"foo"` + Foo *string `json:"foo,omitempty" yaml:"foo,omitempty" mapstructure:"foo,omitempty"` } type AnyOf3_0 struct { diff --git a/tests/data/core/anyOf/anyOf.4.go b/tests/data/core/anyOf/anyOf.4.go index 5fea31b6..4cad4a15 100644 --- a/tests/data/core/anyOf/anyOf.4.go +++ b/tests/data/core/anyOf/anyOf.4.go @@ -13,10 +13,10 @@ type AnyOf4 []AnyOf4Elem type AnyOf4Elem struct { // When consuming a CDEvent, you are consuming a parent event. So, when looking at // the 'from' key, this is the parent's parent. - From EmbeddedlinkendFrom `json:"from" yaml:"from" mapstructure:"from"` + From *EmbeddedlinkendFrom `json:"from,omitempty" yaml:"from,omitempty" mapstructure:"from,omitempty"` // LinkKind corresponds to the JSON schema field "linkKind". - LinkKind string `json:"linkKind" yaml:"linkKind" mapstructure:"linkKind"` + LinkKind *string `json:"linkKind,omitempty" yaml:"linkKind,omitempty" mapstructure:"linkKind,omitempty"` // LinkType corresponds to the JSON schema field "linkType". LinkType EmbeddedlinkendLinkType `json:"linkType" yaml:"linkType" mapstructure:"linkType"` @@ -25,7 +25,7 @@ type AnyOf4Elem struct { Tags EmbeddedlinkendTags `json:"tags,omitempty" yaml:"tags,omitempty" mapstructure:"tags,omitempty"` // Target corresponds to the JSON schema field "target". - Target EmbeddedlinkrelationTarget `json:"target" yaml:"target" mapstructure:"target"` + Target *EmbeddedlinkrelationTarget `json:"target,omitempty" yaml:"target,omitempty" mapstructure:"target,omitempty"` } // UnmarshalYAML implements yaml.Unmarshaler. @@ -515,7 +515,9 @@ func (j *Embeddedlinkrelation) UnmarshalJSON(value []byte) error { } type AnyOf4Elem_2 = Embeddedlinkrelation + type AnyOf4Elem_1 = Embeddedlinkpath + type AnyOf4Elem_0 = Embeddedlinkend // UnmarshalYAML implements yaml.Unmarshaler. diff --git a/tests/data/core/anyOf/anyOf.6.go b/tests/data/core/anyOf/anyOf.6.go index f79b40ae..43c3a81c 100644 --- a/tests/data/core/anyOf/anyOf.6.go +++ b/tests/data/core/anyOf/anyOf.6.go @@ -152,16 +152,27 @@ func (j *Bar2ContentElem) UnmarshalYAML(value *yaml.Node) error { } type Baz2ContentElem_2 = Baz2 + type Baz2ContentElem_0 = Foo2 + type AnyOf6Qux2Elem_2 = Baz2 + type AnyOf6Qux2Elem_1 = Bar2 + type AnyOf6Qux2Elem_0 = Foo2 + type Foo2ContentElem_0 = Foo2 + type Foo2ContentElem_1 = Bar2 + type Foo2ContentElem_2 = Baz2 + type Baz2ContentElem_1 = Bar2 + type Bar2ContentElem_2 = Baz2 + type Bar2ContentElem_1 = Bar2 + type Bar2ContentElem_0 = Foo2 // UnmarshalJSON implements json.Unmarshaler. diff --git a/tests/data/core/anyOf/anyOf.7.go b/tests/data/core/anyOf/anyOf.7.go index 839864a4..21c6ad0e 100644 --- a/tests/data/core/anyOf/anyOf.7.go +++ b/tests/data/core/anyOf/anyOf.7.go @@ -88,6 +88,7 @@ func (j *Item) UnmarshalYAML(value *yaml.Node) error { } type AnyOf7BazElem_0 = Item + type Item struct { // Name corresponds to the JSON schema field "name". Name *string `json:"name,omitempty" yaml:"name,omitempty" mapstructure:"name,omitempty"` @@ -140,6 +141,7 @@ func (j *AnyOf7BazElem) UnmarshalYAML(value *yaml.Node) error { } type AnyOf7BarElem_0 = Item + type AnyOf7Foo struct { // Name corresponds to the JSON schema field "name". Name *string `json:"name,omitempty" yaml:"name,omitempty" mapstructure:"name,omitempty"` diff --git a/tests/data/core/anyOf/anyOf.ref.go b/tests/data/core/anyOf/anyOf.ref.go index f22ef4ae..2361e999 100644 --- a/tests/data/core/anyOf/anyOf.ref.go +++ b/tests/data/core/anyOf/anyOf.ref.go @@ -2,12 +2,127 @@ package test +import "encoding/json" +import "errors" +import "fmt" +import yaml "gopkg.in/yaml.v3" + type Agreement struct { // Id corresponds to the JSON schema field "id". Id *string `json:"id,omitempty" yaml:"id,omitempty" mapstructure:"id,omitempty"` } -type AnyOfRef interface{} +// UnmarshalJSON implements json.Unmarshaler. +func (j *Agreement) UnmarshalJSON(value []byte) error { + type Plain Agreement + var plain Plain + if err := json.Unmarshal(value, &plain); err != nil { + return err + } + *j = Agreement(plain) + return nil +} + +// UnmarshalYAML implements yaml.Unmarshaler. +func (j *Agreement) UnmarshalYAML(value *yaml.Node) error { + type Plain Agreement + var plain Plain + if err := value.Decode(&plain); err != nil { + return err + } + *j = Agreement(plain) + return nil +} + +type AnyOfRef map[string]interface{} + +type AnyOfRef_0 struct { + // Id corresponds to the JSON schema field "id". + Id *string `json:"id,omitempty" yaml:"id,omitempty" mapstructure:"id,omitempty"` + + // Name corresponds to the JSON schema field "name". + Name *string `json:"name,omitempty" yaml:"name,omitempty" mapstructure:"name,omitempty"` +} + +type AnyOfRef_0_0 = Offer + +// UnmarshalYAML implements yaml.Unmarshaler. +func (j *Offer) UnmarshalYAML(value *yaml.Node) error { + type Plain Offer + var plain Plain + if err := value.Decode(&plain); err != nil { + return err + } + *j = Offer(plain) + return nil +} + +// UnmarshalJSON implements json.Unmarshaler. +func (j *Offer) UnmarshalJSON(value []byte) error { + type Plain Offer + var plain Plain + if err := json.Unmarshal(value, &plain); err != nil { + return err + } + *j = Offer(plain) + return nil +} + +type AnyOfRef_0_1 = Agreement + +// UnmarshalJSON implements json.Unmarshaler. +func (j *AnyOfRef_0) UnmarshalJSON(value []byte) error { + var raw map[string]interface{} + if err := json.Unmarshal(value, &raw); err != nil { + return err + } + var anyOfRef_0_0 AnyOfRef_0_0 + var anyOfRef_0_1 AnyOfRef_0_1 + var errs []error + if err := anyOfRef_0_0.UnmarshalJSON(value); err != nil { + errs = append(errs, err) + } + if err := anyOfRef_0_1.UnmarshalJSON(value); err != nil { + errs = append(errs, err) + } + if len(errs) == 2 { + return fmt.Errorf("all validators failed: %s", errors.Join(errs...)) + } + type Plain AnyOfRef_0 + var plain Plain + if err := json.Unmarshal(value, &plain); err != nil { + return err + } + *j = AnyOfRef_0(plain) + return nil +} + +// UnmarshalYAML implements yaml.Unmarshaler. +func (j *AnyOfRef_0) UnmarshalYAML(value *yaml.Node) error { + var raw map[string]interface{} + if err := value.Decode(&raw); err != nil { + return err + } + var anyOfRef_0_0 AnyOfRef_0_0 + var anyOfRef_0_1 AnyOfRef_0_1 + var errs []error + if err := anyOfRef_0_0.UnmarshalYAML(value); err != nil { + errs = append(errs, err) + } + if err := anyOfRef_0_1.UnmarshalYAML(value); err != nil { + errs = append(errs, err) + } + if len(errs) == 2 { + return fmt.Errorf("all validators failed: %s", errors.Join(errs...)) + } + type Plain AnyOfRef_0 + var plain Plain + if err := value.Decode(&plain); err != nil { + return err + } + *j = AnyOfRef_0(plain) + return nil +} type Offer struct { // Name corresponds to the JSON schema field "name". diff --git a/tests/data/core/anyOf/anyOf.ref.json b/tests/data/core/anyOf/anyOf.ref.json index 2a9e4e00..873e93b4 100644 --- a/tests/data/core/anyOf/anyOf.ref.json +++ b/tests/data/core/anyOf/anyOf.ref.json @@ -10,7 +10,7 @@ ], "definitions": { "Policy": { - "oneOf": [ + "anyOf": [ { "$ref": "#/definitions/Offer" }, diff --git a/tests/data/core/anyOf/anyOfMultipleRequired.go b/tests/data/core/anyOf/anyOfMultipleRequired.go new file mode 100644 index 00000000..32a1f712 --- /dev/null +++ b/tests/data/core/anyOf/anyOfMultipleRequired.go @@ -0,0 +1,180 @@ +// Code generated by github.com/atombender/go-jsonschema, DO NOT EDIT. + +package test + +import "encoding/json" +import "errors" +import "fmt" +import "github.com/go-viper/mapstructure/v2" +import yaml "gopkg.in/yaml.v3" +import "reflect" +import "strings" + +type MultipleRequiredBase struct { + // BaseField corresponds to the JSON schema field "baseField". + BaseField string `json:"baseField" yaml:"baseField" mapstructure:"baseField"` +} + +// UnmarshalJSON implements json.Unmarshaler. +func (j *MultipleRequiredBase) UnmarshalJSON(value []byte) error { + var raw map[string]interface{} + if err := json.Unmarshal(value, &raw); err != nil { + return err + } + if _, ok := raw["baseField"]; raw != nil && !ok { + return fmt.Errorf("field baseField in MultipleRequiredBase: required") + } + type Plain MultipleRequiredBase + var plain Plain + if err := json.Unmarshal(value, &plain); err != nil { + return err + } + *j = MultipleRequiredBase(plain) + return nil +} + +// UnmarshalYAML implements yaml.Unmarshaler. +func (j *MultipleRequiredBase) UnmarshalYAML(value *yaml.Node) error { + var raw map[string]interface{} + if err := value.Decode(&raw); err != nil { + return err + } + if _, ok := raw["baseField"]; raw != nil && !ok { + return fmt.Errorf("field baseField in MultipleRequiredBase: required") + } + type Plain MultipleRequiredBase + var plain Plain + if err := value.Decode(&plain); err != nil { + return err + } + *j = MultipleRequiredBase(plain) + return nil +} + +type MultipleRequiredMiddle struct { + // MiddleField corresponds to the JSON schema field "middleField". + MiddleField float64 `json:"middleField" yaml:"middleField" mapstructure:"middleField"` +} + +// UnmarshalJSON implements json.Unmarshaler. +func (j *MultipleRequiredMiddle) UnmarshalJSON(value []byte) error { + var raw map[string]interface{} + if err := json.Unmarshal(value, &raw); err != nil { + return err + } + if _, ok := raw["middleField"]; raw != nil && !ok { + return fmt.Errorf("field middleField in MultipleRequiredMiddle: required") + } + type Plain MultipleRequiredMiddle + var plain Plain + if err := json.Unmarshal(value, &plain); err != nil { + return err + } + *j = MultipleRequiredMiddle(plain) + return nil +} + +// UnmarshalYAML implements yaml.Unmarshaler. +func (j *MultipleRequiredMiddle) UnmarshalYAML(value *yaml.Node) error { + var raw map[string]interface{} + if err := value.Decode(&raw); err != nil { + return err + } + if _, ok := raw["middleField"]; raw != nil && !ok { + return fmt.Errorf("field middleField in MultipleRequiredMiddle: required") + } + type Plain MultipleRequiredMiddle + var plain Plain + if err := value.Decode(&plain); err != nil { + return err + } + *j = MultipleRequiredMiddle(plain) + return nil +} + +type ComposedWithMultipleRequired_0 = MultipleRequiredBase + +type ComposedWithMultipleRequired_1 = MultipleRequiredMiddle + +type ComposedWithMultipleRequired struct { + // BaseField corresponds to the JSON schema field "baseField". + BaseField *string `json:"baseField,omitempty" yaml:"baseField,omitempty" mapstructure:"baseField,omitempty"` + + // DirectField corresponds to the JSON schema field "directField". + DirectField bool `json:"directField" yaml:"directField" mapstructure:"directField"` + + // MiddleField corresponds to the JSON schema field "middleField". + MiddleField *float64 `json:"middleField,omitempty" yaml:"middleField,omitempty" mapstructure:"middleField,omitempty"` + + AdditionalProperties interface{} `mapstructure:",remain"` +} + +// UnmarshalJSON implements json.Unmarshaler. +func (j *ComposedWithMultipleRequired) UnmarshalJSON(value []byte) error { + var raw map[string]interface{} + if err := json.Unmarshal(value, &raw); err != nil { + return err + } + var composedWithMultipleRequired_0 ComposedWithMultipleRequired_0 + var composedWithMultipleRequired_1 ComposedWithMultipleRequired_1 + var errs []error + if err := composedWithMultipleRequired_0.UnmarshalJSON(value); err != nil { + errs = append(errs, err) + } + if err := composedWithMultipleRequired_1.UnmarshalJSON(value); err != nil { + errs = append(errs, err) + } + if len(errs) == 2 { + return fmt.Errorf("all validators failed: %s", errors.Join(errs...)) + } + type Plain ComposedWithMultipleRequired + var plain Plain + if err := json.Unmarshal(value, &plain); err != nil { + return err + } + st := reflect.TypeOf(Plain{}) + for i := range st.NumField() { + delete(raw, st.Field(i).Name) + delete(raw, strings.Split(st.Field(i).Tag.Get("json"), ",")[0]) + } + if err := mapstructure.Decode(raw, &plain.AdditionalProperties); err != nil { + return err + } + *j = ComposedWithMultipleRequired(plain) + return nil +} + +// UnmarshalYAML implements yaml.Unmarshaler. +func (j *ComposedWithMultipleRequired) UnmarshalYAML(value *yaml.Node) error { + var raw map[string]interface{} + if err := value.Decode(&raw); err != nil { + return err + } + var composedWithMultipleRequired_0 ComposedWithMultipleRequired_0 + var composedWithMultipleRequired_1 ComposedWithMultipleRequired_1 + var errs []error + if err := composedWithMultipleRequired_0.UnmarshalYAML(value); err != nil { + errs = append(errs, err) + } + if err := composedWithMultipleRequired_1.UnmarshalYAML(value); err != nil { + errs = append(errs, err) + } + if len(errs) == 2 { + return fmt.Errorf("all validators failed: %s", errors.Join(errs...)) + } + type Plain ComposedWithMultipleRequired + var plain Plain + if err := value.Decode(&plain); err != nil { + return err + } + st := reflect.TypeOf(Plain{}) + for i := range st.NumField() { + delete(raw, st.Field(i).Name) + delete(raw, strings.Split(st.Field(i).Tag.Get("json"), ",")[0]) + } + if err := mapstructure.Decode(raw, &plain.AdditionalProperties); err != nil { + return err + } + *j = ComposedWithMultipleRequired(plain) + return nil +} diff --git a/tests/data/core/anyOf/anyOfMultipleRequired.json b/tests/data/core/anyOf/anyOfMultipleRequired.json new file mode 100644 index 00000000..324e73ad --- /dev/null +++ b/tests/data/core/anyOf/anyOfMultipleRequired.json @@ -0,0 +1,41 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "definitions": { + "MultipleRequiredBase": { + "type": "object", + "properties": { + "baseField": { + "type": "string" + } + }, + "required": ["baseField"] + }, + "MultipleRequiredMiddle": { + "type": "object", + "properties": { + "middleField": { + "type": "number" + } + }, + "required": ["middleField"] + }, + "ComposedWithMultipleRequired": { + "anyOf": [ + { + "$ref": "#/definitions/MultipleRequiredBase" + }, + { + "$ref": "#/definitions/MultipleRequiredMiddle" + } + ], + "type": "object", + "additionalProperties": true, + "properties": { + "directField": { + "type": "boolean" + } + }, + "required": ["directField"] + } + } +} diff --git a/tests/data/core/anyOf/anyOfWithDirectProperties.go b/tests/data/core/anyOf/anyOfWithDirectProperties.go new file mode 100644 index 00000000..d5248703 --- /dev/null +++ b/tests/data/core/anyOf/anyOfWithDirectProperties.go @@ -0,0 +1,126 @@ +// Code generated by github.com/atombender/go-jsonschema, DO NOT EDIT. + +package test + +import "encoding/json" +import "errors" +import "fmt" +import "github.com/go-viper/mapstructure/v2" +import yaml "gopkg.in/yaml.v3" +import "reflect" +import "strings" + +type BaseObject struct { + // BaseField corresponds to the JSON schema field "BaseField". + BaseField string `json:"BaseField" yaml:"BaseField" mapstructure:"BaseField"` +} + +// UnmarshalJSON implements json.Unmarshaler. +func (j *BaseObject) UnmarshalJSON(value []byte) error { + var raw map[string]interface{} + if err := json.Unmarshal(value, &raw); err != nil { + return err + } + if _, ok := raw["BaseField"]; raw != nil && !ok { + return fmt.Errorf("field BaseField in BaseObject: required") + } + type Plain BaseObject + var plain Plain + if err := json.Unmarshal(value, &plain); err != nil { + return err + } + *j = BaseObject(plain) + return nil +} + +// UnmarshalYAML implements yaml.Unmarshaler. +func (j *BaseObject) UnmarshalYAML(value *yaml.Node) error { + var raw map[string]interface{} + if err := value.Decode(&raw); err != nil { + return err + } + if _, ok := raw["BaseField"]; raw != nil && !ok { + return fmt.Errorf("field BaseField in BaseObject: required") + } + type Plain BaseObject + var plain Plain + if err := value.Decode(&plain); err != nil { + return err + } + *j = BaseObject(plain) + return nil +} + +type ComposedWithAllOfAndProperties_0 = BaseObject + +type ComposedWithAllOfAndProperties struct { + // BaseField corresponds to the JSON schema field "BaseField". + BaseField string `json:"BaseField" yaml:"BaseField" mapstructure:"BaseField"` + + // DirectField corresponds to the JSON schema field "DirectField". + DirectField []string `json:"DirectField,omitempty" yaml:"DirectField,omitempty" mapstructure:"DirectField,omitempty"` + + AdditionalProperties interface{} `mapstructure:",remain"` +} + +// UnmarshalJSON implements json.Unmarshaler. +func (j *ComposedWithAllOfAndProperties) UnmarshalJSON(value []byte) error { + var raw map[string]interface{} + if err := json.Unmarshal(value, &raw); err != nil { + return err + } + var composedWithAllOfAndProperties_0 ComposedWithAllOfAndProperties_0 + var errs []error + if err := composedWithAllOfAndProperties_0.UnmarshalJSON(value); err != nil { + errs = append(errs, err) + } + if len(errs) == 1 { + return fmt.Errorf("all validators failed: %s", errors.Join(errs...)) + } + type Plain ComposedWithAllOfAndProperties + var plain Plain + if err := json.Unmarshal(value, &plain); err != nil { + return err + } + st := reflect.TypeOf(Plain{}) + for i := range st.NumField() { + delete(raw, st.Field(i).Name) + delete(raw, strings.Split(st.Field(i).Tag.Get("json"), ",")[0]) + } + if err := mapstructure.Decode(raw, &plain.AdditionalProperties); err != nil { + return err + } + *j = ComposedWithAllOfAndProperties(plain) + return nil +} + +// UnmarshalYAML implements yaml.Unmarshaler. +func (j *ComposedWithAllOfAndProperties) UnmarshalYAML(value *yaml.Node) error { + var raw map[string]interface{} + if err := value.Decode(&raw); err != nil { + return err + } + var composedWithAllOfAndProperties_0 ComposedWithAllOfAndProperties_0 + var errs []error + if err := composedWithAllOfAndProperties_0.UnmarshalYAML(value); err != nil { + errs = append(errs, err) + } + if len(errs) == 1 { + return fmt.Errorf("all validators failed: %s", errors.Join(errs...)) + } + type Plain ComposedWithAllOfAndProperties + var plain Plain + if err := value.Decode(&plain); err != nil { + return err + } + st := reflect.TypeOf(Plain{}) + for i := range st.NumField() { + delete(raw, st.Field(i).Name) + delete(raw, strings.Split(st.Field(i).Tag.Get("json"), ",")[0]) + } + if err := mapstructure.Decode(raw, &plain.AdditionalProperties); err != nil { + return err + } + *j = ComposedWithAllOfAndProperties(plain) + return nil +} diff --git a/tests/data/core/anyOf/anyOfWithDirectProperties.json b/tests/data/core/anyOf/anyOfWithDirectProperties.json new file mode 100644 index 00000000..082f9fd9 --- /dev/null +++ b/tests/data/core/anyOf/anyOfWithDirectProperties.json @@ -0,0 +1,32 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "definitions": { + "BaseObject": { + "type": "object", + "properties": { + "BaseField": { + "type": "string" + } + }, + "required": ["BaseField"] + }, + "ComposedWithAllOfAndProperties": { + "anyOf": [ + { + "$ref": "#/definitions/BaseObject" + } + ], + "type": "object", + "additionalProperties": true, + "properties": { + "DirectField": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + } + } + } + } +} diff --git a/tests/data/core/const/const.go b/tests/data/core/const/const.go new file mode 100644 index 00000000..f4a9b0fc --- /dev/null +++ b/tests/data/core/const/const.go @@ -0,0 +1,159 @@ +// Code generated by github.com/atombender/go-jsonschema, DO NOT EDIT. + +package test + +import "encoding/json" +import "fmt" +import yaml "gopkg.in/yaml.v3" + +type Const struct { + // MyBoolean corresponds to the JSON schema field "myBoolean". + MyBoolean *bool `json:"myBoolean,omitempty" yaml:"myBoolean,omitempty" mapstructure:"myBoolean,omitempty"` + + // MyInteger corresponds to the JSON schema field "myInteger". + MyInteger *int `json:"myInteger,omitempty" yaml:"myInteger,omitempty" mapstructure:"myInteger,omitempty"` + + // MyNumber corresponds to the JSON schema field "myNumber". + MyNumber *float64 `json:"myNumber,omitempty" yaml:"myNumber,omitempty" mapstructure:"myNumber,omitempty"` + + // MyString corresponds to the JSON schema field "myString". + MyString *string `json:"myString,omitempty" yaml:"myString,omitempty" mapstructure:"myString,omitempty"` +} + +// UnmarshalJSON implements json.Unmarshaler. +func (j *Const) UnmarshalJSON(value []byte) error { + type Plain Const + var plain Plain + if err := json.Unmarshal(value, &plain); err != nil { + return err + } + if plain.MyBoolean != nil && *plain.MyBoolean != true { + return fmt.Errorf("field %s: must be equal to %t", "myBoolean", true) + } + if plain.MyInteger != nil && *plain.MyInteger != 42 { + return fmt.Errorf("field %s: must be equal to %v", "myInteger", 42) + } + if plain.MyNumber != nil && *plain.MyNumber != 4.2 { + return fmt.Errorf("field %s: must be equal to %v", "myNumber", 4.2) + } + if plain.MyString != nil && *plain.MyString != "foo" { + return fmt.Errorf("field %s: must be equal to %s", "myString", "foo") + } + *j = Const(plain) + return nil +} + +// UnmarshalYAML implements yaml.Unmarshaler. +func (j *Const) UnmarshalYAML(value *yaml.Node) error { + type Plain Const + var plain Plain + if err := value.Decode(&plain); err != nil { + return err + } + if plain.MyBoolean != nil && *plain.MyBoolean != true { + return fmt.Errorf("field %s: must be equal to %t", "myBoolean", true) + } + if plain.MyInteger != nil && *plain.MyInteger != 42 { + return fmt.Errorf("field %s: must be equal to %v", "myInteger", 42) + } + if plain.MyNumber != nil && *plain.MyNumber != 4.2 { + return fmt.Errorf("field %s: must be equal to %v", "myNumber", 4.2) + } + if plain.MyString != nil && *plain.MyString != "foo" { + return fmt.Errorf("field %s: must be equal to %s", "myString", "foo") + } + *j = Const(plain) + return nil +} + +type Required struct { + // MyBoolean corresponds to the JSON schema field "myBoolean". + MyBoolean bool `json:"myBoolean" yaml:"myBoolean" mapstructure:"myBoolean"` + + // MyInteger corresponds to the JSON schema field "myInteger". + MyInteger int `json:"myInteger" yaml:"myInteger" mapstructure:"myInteger"` + + // MyNumber corresponds to the JSON schema field "myNumber". + MyNumber float64 `json:"myNumber" yaml:"myNumber" mapstructure:"myNumber"` + + // MyString corresponds to the JSON schema field "myString". + MyString string `json:"myString" yaml:"myString" mapstructure:"myString"` +} + +// UnmarshalJSON implements json.Unmarshaler. +func (j *Required) UnmarshalJSON(value []byte) error { + var raw map[string]interface{} + if err := json.Unmarshal(value, &raw); err != nil { + return err + } + if _, ok := raw["myBoolean"]; raw != nil && !ok { + return fmt.Errorf("field myBoolean in Required: required") + } + if _, ok := raw["myInteger"]; raw != nil && !ok { + return fmt.Errorf("field myInteger in Required: required") + } + if _, ok := raw["myNumber"]; raw != nil && !ok { + return fmt.Errorf("field myNumber in Required: required") + } + if _, ok := raw["myString"]; raw != nil && !ok { + return fmt.Errorf("field myString in Required: required") + } + type Plain Required + var plain Plain + if err := json.Unmarshal(value, &plain); err != nil { + return err + } + if plain.MyBoolean != true { + return fmt.Errorf("field %s: must be equal to %t", "myBoolean", true) + } + if plain.MyInteger != 42 { + return fmt.Errorf("field %s: must be equal to %v", "myInteger", 42) + } + if plain.MyNumber != 4.2 { + return fmt.Errorf("field %s: must be equal to %v", "myNumber", 4.2) + } + if plain.MyString != "foo" { + return fmt.Errorf("field %s: must be equal to %s", "myString", "foo") + } + *j = Required(plain) + return nil +} + +// UnmarshalYAML implements yaml.Unmarshaler. +func (j *Required) UnmarshalYAML(value *yaml.Node) error { + var raw map[string]interface{} + if err := value.Decode(&raw); err != nil { + return err + } + if _, ok := raw["myBoolean"]; raw != nil && !ok { + return fmt.Errorf("field myBoolean in Required: required") + } + if _, ok := raw["myInteger"]; raw != nil && !ok { + return fmt.Errorf("field myInteger in Required: required") + } + if _, ok := raw["myNumber"]; raw != nil && !ok { + return fmt.Errorf("field myNumber in Required: required") + } + if _, ok := raw["myString"]; raw != nil && !ok { + return fmt.Errorf("field myString in Required: required") + } + type Plain Required + var plain Plain + if err := value.Decode(&plain); err != nil { + return err + } + if plain.MyBoolean != true { + return fmt.Errorf("field %s: must be equal to %t", "myBoolean", true) + } + if plain.MyInteger != 42 { + return fmt.Errorf("field %s: must be equal to %v", "myInteger", 42) + } + if plain.MyNumber != 4.2 { + return fmt.Errorf("field %s: must be equal to %v", "myNumber", 4.2) + } + if plain.MyString != "foo" { + return fmt.Errorf("field %s: must be equal to %s", "myString", "foo") + } + *j = Required(plain) + return nil +} diff --git a/tests/data/core/const/const.json b/tests/data/core/const/const.json new file mode 100644 index 00000000..f8c52592 --- /dev/null +++ b/tests/data/core/const/const.json @@ -0,0 +1,52 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "id": "https://example.com/primitives", + "type": "object", + "properties": { + "myString": { + "type": "string", + "const": "foo" + }, + "myNumber": { + "type": "number", + "const": 4.2 + }, + "myInteger": { + "type": "integer", + "const": 42 + }, + "myBoolean": { + "type": "boolean", + "const": true + } + }, + "$defs": { + "Required": { + "type": "object", + "properties": { + "myString": { + "type": "string", + "const": "foo" + }, + "myNumber": { + "type": "number", + "const": 4.2 + }, + "myInteger": { + "type": "integer", + "const": 42 + }, + "myBoolean": { + "type": "boolean", + "const": true + } + }, + "required": [ + "myString", + "myNumber", + "myInteger", + "myBoolean" + ] + } + } +} \ No newline at end of file diff --git a/tests/data/core/refExternalFileAllOfNestedRefs/refExternalFileAllOfNestedRefs.go b/tests/data/core/refExternalFileAllOfNestedRefs/refExternalFileAllOfNestedRefs.go new file mode 100644 index 00000000..61bd2b91 --- /dev/null +++ b/tests/data/core/refExternalFileAllOfNestedRefs/refExternalFileAllOfNestedRefs.go @@ -0,0 +1,20 @@ +// Code generated by github.com/atombender/go-jsonschema, DO NOT EDIT. + +package test + +type Name string + +type RefExternalFileAllOfNestedRefs struct { + // Name corresponds to the JSON schema field "name". + Name *Name `json:"name,omitempty" yaml:"name,omitempty" mapstructure:"name,omitempty"` +} + +type RefNested struct { + // MyThing corresponds to the JSON schema field "myThing". + MyThing *Thing `json:"myThing,omitempty" yaml:"myThing,omitempty" mapstructure:"myThing,omitempty"` +} + +type Thing struct { + // Name corresponds to the JSON schema field "name". + Name *Name `json:"name,omitempty" yaml:"name,omitempty" mapstructure:"name,omitempty"` +} diff --git a/tests/data/core/refExternalFileAllOfNestedRefs/refExternalFileAllOfNestedRefs.json b/tests/data/core/refExternalFileAllOfNestedRefs/refExternalFileAllOfNestedRefs.json new file mode 100644 index 00000000..352ddc63 --- /dev/null +++ b/tests/data/core/refExternalFileAllOfNestedRefs/refExternalFileAllOfNestedRefs.json @@ -0,0 +1,10 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "id": "https://example.com/refExternalFileAllOfNestedRefs", + "type": "object", + "allOf": [ + { + "$ref": "../refNested/refNested.json#/$defs/Thing" + } + ] +} diff --git a/tests/data/core/refExternalFileNestedRefs/refExternalFileNestedRefs.go b/tests/data/core/refExternalFileNestedRefs/refExternalFileNestedRefs.go new file mode 100644 index 00000000..93dc6b10 --- /dev/null +++ b/tests/data/core/refExternalFileNestedRefs/refExternalFileNestedRefs.go @@ -0,0 +1,20 @@ +// Code generated by github.com/atombender/go-jsonschema, DO NOT EDIT. + +package test + +type Name string + +type RefExternalFileNestedRefs struct { + // MyExternalThing corresponds to the JSON schema field "myExternalThing". + MyExternalThing *Thing `json:"myExternalThing,omitempty" yaml:"myExternalThing,omitempty" mapstructure:"myExternalThing,omitempty"` +} + +type RefNested struct { + // MyThing corresponds to the JSON schema field "myThing". + MyThing *Thing `json:"myThing,omitempty" yaml:"myThing,omitempty" mapstructure:"myThing,omitempty"` +} + +type Thing struct { + // Name corresponds to the JSON schema field "name". + Name *Name `json:"name,omitempty" yaml:"name,omitempty" mapstructure:"name,omitempty"` +} diff --git a/tests/data/core/refExternalFileNestedRefs/refExternalFileNestedRefs.json b/tests/data/core/refExternalFileNestedRefs/refExternalFileNestedRefs.json new file mode 100644 index 00000000..b36c9302 --- /dev/null +++ b/tests/data/core/refExternalFileNestedRefs/refExternalFileNestedRefs.json @@ -0,0 +1,10 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "id": "https://example.com/refExternalFileNestedRefs", + "type": "object", + "properties": { + "myExternalThing": { + "$ref": "../refNested/refNested.json#/$defs/Thing" + } + } +} diff --git a/tests/data/core/refNested/refNested.go b/tests/data/core/refNested/refNested.go new file mode 100644 index 00000000..d917b343 --- /dev/null +++ b/tests/data/core/refNested/refNested.go @@ -0,0 +1,15 @@ +// Code generated by github.com/atombender/go-jsonschema, DO NOT EDIT. + +package test + +type Name string + +type RefNested struct { + // MyThing corresponds to the JSON schema field "myThing". + MyThing *Thing `json:"myThing,omitempty" yaml:"myThing,omitempty" mapstructure:"myThing,omitempty"` +} + +type Thing struct { + // Name corresponds to the JSON schema field "name". + Name *Name `json:"name,omitempty" yaml:"name,omitempty" mapstructure:"name,omitempty"` +} diff --git a/tests/data/core/refNested/refNested.json b/tests/data/core/refNested/refNested.json new file mode 100644 index 00000000..72c35f33 --- /dev/null +++ b/tests/data/core/refNested/refNested.json @@ -0,0 +1,23 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "id": "https://example.com/refNested", + "type": "object", + "properties": { + "myThing": { + "$ref": "#/$defs/Thing" + } + }, + "$defs": { + "Thing": { + "type": "object", + "properties": { + "name": { + "$ref": "#/$defs/Name" + } + } + }, + "Name": { + "type": "string" + } + } +} diff --git a/tests/data/crossPackageAllOf/other/other.go b/tests/data/crossPackageAllOf/other/other.go new file mode 100644 index 00000000..85461a98 --- /dev/null +++ b/tests/data/crossPackageAllOf/other/other.go @@ -0,0 +1,10 @@ +// Code generated by github.com/atombender/go-jsonschema, DO NOT EDIT. + +package other + +type Thing struct { + // Value corresponds to the JSON schema field "value". + Value *Value `json:"value,omitempty" yaml:"value,omitempty" mapstructure:"value,omitempty"` +} + +type Value float64 diff --git a/tests/data/crossPackageAllOf/other/other.json b/tests/data/crossPackageAllOf/other/other.json new file mode 100644 index 00000000..8e8debde --- /dev/null +++ b/tests/data/crossPackageAllOf/other/other.json @@ -0,0 +1,17 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "id": "https://example.com/other", + "$defs": { + "Thing": { + "type": "object", + "properties": { + "value": { + "$ref": "#/$defs/Value" + } + } + }, + "Value": { + "type": "number" + } + } +} diff --git a/tests/data/crossPackageAllOf/schema/schema.go b/tests/data/crossPackageAllOf/schema/schema.go new file mode 100644 index 00000000..9ddace31 --- /dev/null +++ b/tests/data/crossPackageAllOf/schema/schema.go @@ -0,0 +1,30 @@ +// Code generated by github.com/atombender/go-jsonschema, DO NOT EDIT. + +package schema + +import other "github.com/atombender/go-jsonschema/tests/data/crossPackageAllOf/other" + +type Name string + +type Schema struct { + // DefInOtherSchema corresponds to the JSON schema field "defInOtherSchema". + DefInOtherSchema *SchemaDefInOtherSchema `json:"defInOtherSchema,omitempty" yaml:"defInOtherSchema,omitempty" mapstructure:"defInOtherSchema,omitempty"` + + // DefInSameSchema corresponds to the JSON schema field "defInSameSchema". + DefInSameSchema *SchemaDefInSameSchema `json:"defInSameSchema,omitempty" yaml:"defInSameSchema,omitempty" mapstructure:"defInSameSchema,omitempty"` +} + +type SchemaDefInOtherSchema struct { + // Value corresponds to the JSON schema field "value". + Value *other.Value `json:"value,omitempty" yaml:"value,omitempty" mapstructure:"value,omitempty"` +} + +type SchemaDefInSameSchema struct { + // Name corresponds to the JSON schema field "name". + Name *Name `json:"name,omitempty" yaml:"name,omitempty" mapstructure:"name,omitempty"` +} + +type Thing struct { + // Name corresponds to the JSON schema field "name". + Name *Name `json:"name,omitempty" yaml:"name,omitempty" mapstructure:"name,omitempty"` +} diff --git a/tests/data/crossPackageAllOf/schema/schema.json b/tests/data/crossPackageAllOf/schema/schema.json new file mode 100644 index 00000000..2bdaf68c --- /dev/null +++ b/tests/data/crossPackageAllOf/schema/schema.json @@ -0,0 +1,36 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "id": "https://example.com/schema", + "type": "object", + "properties": { + "defInSameSchema": { + "type": "object", + "allOf": [ + { + "$ref": "#/$defs/Thing" + } + ] + }, + "defInOtherSchema": { + "type": "object", + "allOf": [ + { + "$ref": "../other/other.json#/$defs/Thing" + } + ] + } + }, + "$defs": { + "Thing": { + "type": "object", + "properties": { + "name": { + "$ref": "#/$defs/Name" + } + } + }, + "Name": { + "type": "string" + } + } +} \ No newline at end of file diff --git a/tests/data/preferOmitzero/preferOmitzero.go b/tests/data/preferOmitzero/preferOmitzero.go new file mode 100644 index 00000000..d090fe7c --- /dev/null +++ b/tests/data/preferOmitzero/preferOmitzero.go @@ -0,0 +1,89 @@ +// Code generated by github.com/atombender/go-jsonschema, DO NOT EDIT. + +package test + +import "encoding/json" +import "fmt" +import yaml "gopkg.in/yaml.v3" + +type PreferOmitzero struct { + // MyArray corresponds to the JSON schema field "myArray". + MyArray []interface{} `json:"myArray,omitzero"` + + // MyBoolean corresponds to the JSON schema field "myBoolean". + MyBoolean *bool `json:"myBoolean,omitzero"` + + // MyInteger corresponds to the JSON schema field "myInteger". + MyInteger *int `json:"myInteger,omitzero"` + + // MyMap corresponds to the JSON schema field "myMap". + MyMap PreferOmitzeroMyMap `json:"myMap,omitzero"` + + // MyNull corresponds to the JSON schema field "myNull". + MyNull interface{} `json:"myNull,omitzero"` + + // MyNullArray corresponds to the JSON schema field "myNullArray". + MyNullArray []interface{} `json:"myNullArray,omitzero"` + + // MyNumber corresponds to the JSON schema field "myNumber". + MyNumber *float64 `json:"myNumber,omitzero"` + + // MyObjectArray corresponds to the JSON schema field "myObjectArray". + MyObjectArray []PreferOmitzeroMyObjectArrayElem `json:"myObjectArray,omitzero"` + + // MyString corresponds to the JSON schema field "myString". + MyString *string `json:"myString,omitzero"` + + // MyStringArray corresponds to the JSON schema field "myStringArray". + MyStringArray []string `json:"myStringArray,omitzero"` +} + +type PreferOmitzeroMyMap map[string]float64 + +type PreferOmitzeroMyObjectArrayElem map[string]interface{} + +// UnmarshalJSON implements json.Unmarshaler. +func (j *PreferOmitzero) UnmarshalJSON(value []byte) error { + var raw map[string]interface{} + if err := json.Unmarshal(value, &raw); err != nil { + return err + } + type Plain PreferOmitzero + var plain Plain + if err := json.Unmarshal(value, &plain); err != nil { + return err + } + if plain.MyNull != nil { + return fmt.Errorf("field %s: must be null", "myNull") + } + for i0 := range plain.MyNullArray { + if plain.MyNullArray[i0] != nil { + return fmt.Errorf("field %s: must be null", fmt.Sprintf("myNullArray[%d]", i0)) + } + } + *j = PreferOmitzero(plain) + return nil +} + +// UnmarshalYAML implements yaml.Unmarshaler. +func (j *PreferOmitzero) UnmarshalYAML(value *yaml.Node) error { + var raw map[string]interface{} + if err := value.Decode(&raw); err != nil { + return err + } + type Plain PreferOmitzero + var plain Plain + if err := value.Decode(&plain); err != nil { + return err + } + if plain.MyNull != nil { + return fmt.Errorf("field %s: must be null", "myNull") + } + for i0 := range plain.MyNullArray { + if plain.MyNullArray[i0] != nil { + return fmt.Errorf("field %s: must be null", fmt.Sprintf("myNullArray[%d]", i0)) + } + } + *j = PreferOmitzero(plain) + return nil +} diff --git a/tests/data/preferOmitzero/preferOmitzero.json b/tests/data/preferOmitzero/preferOmitzero.json new file mode 100644 index 00000000..c3ef2a4b --- /dev/null +++ b/tests/data/preferOmitzero/preferOmitzero.json @@ -0,0 +1,49 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "id": "https://example.com/primitives", + "type": "object", + "properties": { + "myString": { + "type": "string" + }, + "myNumber": { + "type": "number" + }, + "myInteger": { + "type": "integer" + }, + "myBoolean": { + "type": "boolean" + }, + "myNull": { + "type": "null" + }, + "myStringArray": { + "type": "array", + "items": { + "type": "string" + } + }, + "myNullArray": { + "type": "array", + "items": { + "type": "null" + } + }, + "myObjectArray": { + "type": "array", + "items": { + "type": "object" + } + }, + "myArray": { + "type": "array" + }, + "myMap": { + "type": "object", + "additionalProperties": { + "type": "number" + } + } + } +} \ No newline at end of file diff --git a/tests/data/regressions/issue427/issue427.go b/tests/data/regressions/issue427/issue427.go index ebfc2a7c..815dfbda 100644 --- a/tests/data/regressions/issue427/issue427.go +++ b/tests/data/regressions/issue427/issue427.go @@ -10,7 +10,7 @@ import "reflect" type TestcaseExpectedPeerName_0 = PeerName -// Represents a peer (i.e., end entity) certificate's name (Subject or SAN). +// For server (i.e. client-side) validation: the expected peer name, if any type TestcaseExpectedPeerName struct { // The kind of peer name Kind PeerKind `json:"kind" yaml:"kind" mapstructure:"kind"` @@ -179,7 +179,7 @@ func (j *TestcaseExpectedPeerName) UnmarshalJSON(value []byte) error { return nil } -// Represents a peer (i.e., end entity) certificate's name (Subject or SAN). +// For server (i.e. client-side) validation: the expected peer name, if any type TestcaseNotExpectedPeerName struct { // The kind of peer name Kind PeerKind `json:"kind" yaml:"kind" mapstructure:"kind"` diff --git a/tests/generation_test.go b/tests/generation_test.go index 214133a3..de94bc34 100644 --- a/tests/generation_test.go +++ b/tests/generation_test.go @@ -97,6 +97,25 @@ func TestCrossPackage(t *testing.T) { testExampleFile(t, cfg, "./data/crossPackage/schema/schema.json") } +func TestCrossPackageAllOf(t *testing.T) { + t.Parallel() + + cfg := basicConfig + cfg.SchemaMappings = []generator.SchemaMapping{ + { + SchemaID: "https://example.com/schema", + PackageName: "github.com/atombender/go-jsonschema/tests/helpers/schema", + OutputName: "schema.go", + }, + { + SchemaID: "https://example.com/other", + PackageName: "github.com/atombender/go-jsonschema/tests/data/crossPackageAllOf/other", + OutputName: "../other/other.go", + }, + } + testExampleFile(t, cfg, "./data/crossPackageAllOf/schema/schema.json") +} + func TestCrossPackageNoOutput(t *testing.T) { t.Parallel() @@ -208,6 +227,25 @@ func TestMinSizeInt(t *testing.T) { testExamples(t, cfg, "./data/minSizedInts") } +func TestAliasSingleAllOfAnyOfRefs(t *testing.T) { + t.Parallel() + + cfg := basicConfig + cfg.AliasSingleAllOfAnyOfRefs = true + + testExamples(t, cfg, "./data/aliasSingleAllOfAnyOfRefs") +} + +func TestPreferOmitzero(t *testing.T) { + t.Parallel() + + cfg := basicConfig + cfg.PreferOmitzero = true + cfg.Tags = []string{"json"} + + testExamples(t, cfg, "./data/preferOmitzero") +} + func TestSchemaExtensions(t *testing.T) { t.Parallel() diff --git a/tests/unmarshal_json_test.go b/tests/unmarshal_json_test.go index e569151a..b576fd08 100644 --- a/tests/unmarshal_json_test.go +++ b/tests/unmarshal_json_test.go @@ -85,8 +85,8 @@ func TestJsonUmarshalAnyOf(t *testing.T) { t, &testAnyOf.AnyOf1{ Configurations: []testAnyOf.AnyOf1ConfigurationsElem{ - {Foo: "hello"}, - {Bar: 2.2}, + {Foo: ptr("hello")}, + {Bar: ptr(2.2)}, {Baz: ptr(true)}, }, Flags: "hello", @@ -115,8 +115,8 @@ func TestJsonUmarshalAnyOf(t *testing.T) { t, &testAnyOf.AnyOf1{ Configurations: []testAnyOf.AnyOf1ConfigurationsElem{ - {Foo: "ciao"}, - {Bar: 200.0}, + {Foo: ptr("ciao")}, + {Bar: ptr(200.0)}, }, Flags: true, }, @@ -145,8 +145,8 @@ func TestJsonUmarshalAnyOf(t *testing.T) { t, &testAnyOf.AnyOf2{ Configurations: []testAnyOf.AnyOf2ConfigurationsElem{ - {Foo: "ciao"}, - {Bar: 2.0}, + {Foo: ptr("ciao")}, + {Bar: ptr(2.0)}, {Baz: ptr(false)}, }, }, @@ -162,7 +162,7 @@ func TestJsonUmarshalAnyOf(t *testing.T) { assert.Equal( t, &testAnyOf.AnyOf3{ - Foo: "ciao", + Foo: ptr("ciao"), }, target, ) @@ -176,7 +176,7 @@ func TestJsonUmarshalAnyOf(t *testing.T) { assert.Equal( t, &testAnyOf.AnyOf3{ - Bar: 2.0, + Bar: ptr(2.0), }, target, )