Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement map to retain original struct field names in schema #117

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 11 additions & 8 deletions reflect.go
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,7 @@ func (r *Reflector) reflectStruct(definitions Definitions, t reflect.Type, s *Sc
r.addDefinition(definitions, t, s)
s.Type = "object"
s.Properties = NewProperties()
s.OriginalPropertiesMapping = make(map[string]string)
s.Description = r.lookupComment(t, "")
if r.AssignAnchor {
s.Anchor = t.Name()
Expand Down Expand Up @@ -492,7 +493,7 @@ func (r *Reflector) reflectStructFields(st *Schema, definitions Definitions, t r
}

handleField := func(f reflect.StructField) {
name, shouldEmbed, required, nullable := r.reflectFieldName(f)
name, originalName, shouldEmbed, required, nullable := r.reflectFieldName(f)
// if anonymous and exported type should be processed recursively
// current type should inherit properties of anonymous one
if name == "" {
Expand Down Expand Up @@ -531,6 +532,7 @@ func (r *Reflector) reflectStructFields(st *Schema, definitions Definitions, t r
}

st.Properties.Set(name, property)
st.OriginalPropertiesMapping[originalName] = name
if required {
st.Required = appendUniqueString(st.Required, name)
}
Expand Down Expand Up @@ -1010,17 +1012,17 @@ func (r *Reflector) fieldNameTag() string {
return "json"
}

func (r *Reflector) reflectFieldName(f reflect.StructField) (string, bool, bool, bool) {
func (r *Reflector) reflectFieldName(f reflect.StructField) (string, string, bool, bool, bool) {
jsonTagString := f.Tag.Get(r.fieldNameTag())
jsonTags := strings.Split(jsonTagString, ",")

if ignoredByJSONTags(jsonTags) {
return "", false, false, false
return "", "", false, false, false
}

schemaTags := strings.Split(f.Tag.Get("jsonschema"), ",")
if ignoredByJSONSchemaTags(schemaTags) {
return "", false, false, false
return "", "", false, false, false
}

var required bool
Expand All @@ -1034,22 +1036,23 @@ func (r *Reflector) reflectFieldName(f reflect.StructField) (string, bool, bool,
if f.Anonymous && jsonTags[0] == "" {
// As per JSON Marshal rules, anonymous structs are inherited
if f.Type.Kind() == reflect.Struct {
return "", true, false, false
return "", "", true, false, false
}

// As per JSON Marshal rules, anonymous pointer to structs are inherited
if f.Type.Kind() == reflect.Ptr && f.Type.Elem().Kind() == reflect.Struct {
return "", true, false, false
return "", "", true, false, false
}
}

// As per JSON Marshal rules, inline nested structs that have `inline` tag.
if inlinedByJSONTags(jsonTags) {
return "", true, false, false
return "", "", true, false, false
}

// Try to determine the name from the different combos
name := f.Name
originalName := f.Name
if jsonTags[0] != "" {
name = jsonTags[0]
}
Expand All @@ -1060,7 +1063,7 @@ func (r *Reflector) reflectFieldName(f reflect.StructField) (string, bool, bool,
name = r.KeyNamer(name)
}

return name, false, required, nullable
return name, originalName, false, required, nullable
}

// UnmarshalJSON is used to parse a schema object or boolean.
Expand Down
12 changes: 12 additions & 0 deletions reflect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -675,3 +675,15 @@ func TestJSONSchemaAlias(t *testing.T) {
compareSchemaOutput(t, "fixtures/schema_alias.json", r, &AliasObjectB{})
compareSchemaOutput(t, "fixtures/schema_alias_2.json", r, &AliasObjectC{})
}

func TestOriginalPropertiesMapping(t *testing.T) {
type TestStruct struct {
OriginalName string `json:"altered_name"`
}

r := &Reflector{}

s := r.Reflect(&TestStruct{})
subschema := s.Definitions["TestStruct"]
assert.EqualValues(t, "altered_name", subschema.OriginalPropertiesMapping["OriginalName"])
}
1 change: 1 addition & 0 deletions schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ type Schema struct {
// RFC draft-bhutton-json-schema-00 section 10.3.2 (sub-schemas)
Properties *orderedmap.OrderedMap[string, *Schema] `json:"properties,omitempty"` // section 10.3.2.1
PatternProperties map[string]*Schema `json:"patternProperties,omitempty"` // section 10.3.2.2
OriginalPropertiesMapping map[string]string `json:"-"`
AdditionalProperties *Schema `json:"additionalProperties,omitempty"` // section 10.3.2.3
PropertyNames *Schema `json:"propertyNames,omitempty"` // section 10.3.2.4
// RFC draft-bhutton-json-schema-validation-00, section 6
Expand Down