Skip to content

Commit 704d21b

Browse files
committed
Fill in embedded structs when unmarshaling
Closes #25
1 parent a26428e commit 704d21b

File tree

2 files changed

+60
-17
lines changed

2 files changed

+60
-17
lines changed

decoder.go

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,17 @@ func (d *decoder) unmarshalMap(size uint, offset uint, result reflect.Value) (ui
250250
newOffset, err := d.decodeMap(size, offset, rv)
251251
result.Set(rv)
252252
return newOffset, err
253+
case reflect.Ptr:
254+
// XXX - This duplicate Ptr hanlding code exists because decodeMap
255+
// calls unmarshalMap directly when handling embeded structs. It
256+
// would be nice to clean this up.
257+
for result.Kind() == reflect.Ptr {
258+
if result.IsNil() {
259+
result.Set(reflect.New(result.Type().Elem()))
260+
}
261+
result = result.Elem()
262+
}
263+
return d.unmarshalMap(size, offset, result)
253264
}
254265
}
255266

@@ -450,8 +461,13 @@ func (d *decoder) decodeString(size uint, offset uint) (string, uint, error) {
450461
return string(d.buffer[offset:newOffset]), newOffset, nil
451462
}
452463

464+
type fieldsType struct {
465+
namedFields map[string]int
466+
anonymousFields []int
467+
}
468+
453469
var (
454-
fieldMap = map[reflect.Type]map[string]int{}
470+
fieldMap = map[reflect.Type]*fieldsType{}
455471
fieldMapMu sync.RWMutex
456472
)
457473

@@ -463,21 +479,39 @@ func (d *decoder) decodeStruct(size uint, offset uint, result reflect.Value) (ui
463479
fieldMapMu.RUnlock()
464480
if !ok {
465481
numFields := resultType.NumField()
466-
fields = make(map[string]int, numFields)
482+
namedFields := make(map[string]int, numFields)
483+
var anonymous []int
467484
for i := 0; i < numFields; i++ {
468-
fieldType := resultType.Field(i)
485+
field := resultType.Field(i)
469486

470-
fieldName := fieldType.Name
471-
if tag := fieldType.Tag.Get("maxminddb"); tag != "" {
487+
fieldName := field.Name
488+
if tag := field.Tag.Get("maxminddb"); tag != "" {
489+
if tag == "-" {
490+
continue
491+
}
472492
fieldName = tag
473493
}
474-
fields[fieldName] = i
494+
if field.Anonymous {
495+
anonymous = append(anonymous, i)
496+
continue
497+
}
498+
namedFields[fieldName] = i
475499
}
476500
fieldMapMu.Lock()
501+
fields = &fieldsType{namedFields, anonymous}
477502
fieldMap[resultType] = fields
478503
fieldMapMu.Unlock()
479504
}
480505

506+
// This fills in embedded structs
507+
for i := range fields.anonymousFields {
508+
_, err := d.unmarshalMap(size, offset, result.Field(i))
509+
if err != nil {
510+
return 0, err
511+
}
512+
}
513+
514+
// This handles named fields
481515
for i := uint(0); i < size; i++ {
482516
var (
483517
err error
@@ -487,12 +521,13 @@ func (d *decoder) decodeStruct(size uint, offset uint, result reflect.Value) (ui
487521
if err != nil {
488522
return 0, err
489523
}
490-
i, ok := fields[key]
524+
j, ok := fields.namedFields[key]
491525
if !ok {
492526
offset = d.nextValueOffset(offset, 1)
493527
continue
494528
}
495-
offset, err = d.decode(offset, result.Field(i))
529+
530+
offset, err = d.decode(offset, result.Field(j))
496531
if err != nil {
497532
return 0, err
498533
}

reader_test.go

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,19 @@ func (s *MySuite) TestDecoder(c *C) {
162162
c.Assert(reader.Close(), IsNil)
163163
}
164164

165+
type NestedMapX struct {
166+
UTF8StringX string `maxminddb:"utf8_stringX"`
167+
}
168+
169+
type NestedPointerMapX struct {
170+
ArrayX []int `maxminddb:"arrayX"`
171+
}
172+
165173
type PointerMap struct {
166-
MapX map[string]interface{} `maxminddb:"mapX"`
174+
MapX struct {
175+
NestedMapX
176+
*NestedPointerMapX
177+
} `maxminddb:"mapX"`
167178
}
168179

169180
type TestPointerType struct {
@@ -183,14 +194,13 @@ type TestPointerType struct {
183194
Utf8String *string `maxminddb:"utf8_string"`
184195
}
185196

186-
func (s *MySuite) TestStructWithPointer(c *C) {
197+
func (s *MySuite) TestComplexStructWithNestingAndPointer(c *C) {
187198
reader, err := Open("test-data/test-data/MaxMind-DB-test-decoder.mmdb")
188199
c.Assert(err, IsNil)
189200

190201
var result TestPointerType
191202

192-
c.Log("Before")
193-
reader.Lookup(net.ParseIP("::1.1.1.0"), &result)
203+
err = reader.Lookup(net.ParseIP("::1.1.1.0"), &result)
194204
c.Assert(err, IsNil)
195205

196206
c.Assert(*result.Array, DeepEquals, []uint{uint(1), uint(2), uint(3)})
@@ -200,11 +210,9 @@ func (s *MySuite) TestStructWithPointer(c *C) {
200210
c.Assert(*result.Float, Equals, float32(1.1))
201211
c.Assert(*result.Int32, Equals, int32(-268435456))
202212

203-
c.Assert(result.Map.MapX, DeepEquals,
204-
map[string]interface{}{
205-
"arrayX": []interface{}{uint64(7), uint64(8), uint64(9)},
206-
"utf8_stringX": "hello",
207-
})
213+
c.Assert(result.Map.MapX.ArrayX, DeepEquals, []int{7, 8, 9})
214+
215+
c.Assert(result.Map.MapX.UTF8StringX, Equals, "hello")
208216

209217
c.Assert(*result.Uint16, Equals, uint16(100))
210218
c.Assert(*result.Uint32, Equals, uint32(268435456))

0 commit comments

Comments
 (0)