Skip to content

Commit 298f023

Browse files
Apply naming convention to nested structs with dynamorm:"json" tag
Updates marshalComplexValue to use the same naming convention logic as top-level fields when marshaling nested structs. This ensures consistent camelCase field names throughout the DynamoDB document. Previously, nested structs with the dynamorm:"json" tag would use Go struct field names directly (PascalCase), while top-level fields were converted to camelCase. Now both follow the same pattern: 1. Apply naming convention (e.g., PascalCase → camelCase) 2. Override with json tag if present Example: - Field: IsInstructionallyFunded - Naming convention: isInstructionallyFunded - With json:"is_funded": is_funded (tag overrides convention) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 49593c3 commit 298f023

File tree

1 file changed

+23
-14
lines changed

1 file changed

+23
-14
lines changed

pkg/marshal/marshaler.go

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111

1212
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
1313
"github.com/pay-theory/dynamorm/pkg/model"
14+
"github.com/pay-theory/dynamorm/pkg/naming"
1415
pkgTypes "github.com/pay-theory/dynamorm/pkg/types"
1516
)
1617

@@ -20,6 +21,8 @@ type Marshaler struct {
2021
cache sync.Map // map[reflect.Type]*structMarshaler
2122
// Optional custom converter registry shared with DB
2223
converter *pkgTypes.Converter
24+
// Naming convention for nested structs (defaults to CamelCase)
25+
namingConvention naming.Convention
2326
}
2427

2528
// structMarshaler contains cached information for marshaling a specific struct type
@@ -80,6 +83,11 @@ func (m *Marshaler) MarshalItem(model any, metadata *model.Metadata) (map[string
8083
return nil, fmt.Errorf("model must be a struct or pointer to struct")
8184
}
8285

86+
// Set naming convention from metadata for nested struct marshaling
87+
if metadata != nil {
88+
m.namingConvention = metadata.NamingConvention
89+
}
90+
8391
// Get or create cached marshaler
8492
typ := v.Type()
8593
cached, ok := m.cache.Load(typ)
@@ -426,23 +434,24 @@ func (m *Marshaler) marshalComplexValue(v reflect.Value) (types.AttributeValue,
426434
return nil, fmt.Errorf("struct field %s: %w", field.Name, err)
427435
}
428436

429-
// Parse JSON tag to get the field name
430-
fieldName := field.Name
437+
// Use same logic as top-level fields: naming convention first, then override with json tag if present
438+
fieldName := naming.ConvertAttrName(field.Name, m.namingConvention)
439+
440+
// Check for json tag override
431441
if jsonTag := field.Tag.Get("json"); jsonTag != "" && jsonTag != "-" {
432-
// Handle json tag with options like "fieldname,omitempty"
433-
if commaIdx := 0; commaIdx < len(jsonTag) {
434-
for j, c := range jsonTag {
435-
if c == ',' {
436-
commaIdx = j
437-
break
438-
}
439-
}
440-
if commaIdx > 0 {
441-
fieldName = jsonTag[:commaIdx]
442-
} else if jsonTag != "" {
443-
fieldName = jsonTag
442+
// Parse json tag, handling options like "fieldname,omitempty"
443+
commaIdx := -1
444+
for j, c := range jsonTag {
445+
if c == ',' {
446+
commaIdx = j
447+
break
444448
}
445449
}
450+
if commaIdx > 0 {
451+
fieldName = jsonTag[:commaIdx]
452+
} else {
453+
fieldName = jsonTag
454+
}
446455
}
447456

448457
structMap[fieldName] = av

0 commit comments

Comments
 (0)