Skip to content

Commit 7861729

Browse files
committed
add mapping to existing structs
1 parent 70b19ee commit 7861729

File tree

3 files changed

+230
-71
lines changed

3 files changed

+230
-71
lines changed

README.md

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Main features:
1111
* Removing existing fields from struct
1212
* Modifying fields' types and tags
1313
* Easy reading of dynamic structs
14+
* Mapping dynamic struct with set values to existing struct
1415

1516
Works out-of-the-box with:
1617
* https://github.com/go-playground/form
@@ -210,25 +211,31 @@ import (
210211
"github.com/ompluscator/dynamic-struct"
211212
)
212213

214+
type DataOne struct {
215+
Integer int `json:"int"`
216+
Text string `json:"someText"`
217+
Float float64 `json:"double"`
218+
}
219+
220+
type DataTwo struct {
221+
Boolean bool
222+
Slice []int
223+
Anonymous string `json:"-"`
224+
}
225+
213226
func main() {
214-
instance := dynamicstruct.NewStruct().
215-
AddField("Integer", 0, `json:"int"`).
216-
AddField("Text", "", `json:"someText"`).
217-
AddField("Float", 0.0, `json:"double"`).
218-
AddField("Boolean", false, "").
219-
AddField("Slice", []int{}, "").
220-
AddField("Anonymous", "", `json:"-"`).
227+
instance := dynamicstruct.MergeStructs(DataOne{}, DataTwo{}).
221228
Build().
222229
New()
223230

224231
data := []byte(`
225232
{
226-
"int": 123,
227-
"someText": "example",
228-
"double": 123.45,
229-
"Boolean": true,
230-
"Slice": [1, 2, 3],
231-
"Anonymous": "avoid to read"
233+
"int": 123,
234+
"someText": "example",
235+
"double": 123.45,
236+
"Boolean": true,
237+
"Slice": [1, 2, 3],
238+
"Anonymous": "avoid to read"
232239
}
233240
`)
234241

@@ -237,20 +244,30 @@ func main() {
237244
log.Fatal(err)
238245
}
239246

240-
value := dynamicstruct.NewReader(instance)
241-
fmt.Println("Integer", value.GetField("Integer").Int())
242-
fmt.Println("Text", value.GetField("Text").String())
243-
fmt.Println("Float", value.GetField("Float").Float64())
244-
fmt.Println("Boolean", value.GetField("Boolean").Bool())
245-
fmt.Println("Slice", value.GetField("Slice").Interface().([]int))
246-
fmt.Println("Anonymous", value.GetField("Anonymous").String())
247+
reader := dynamicstruct.NewReader(instance)
248+
249+
fmt.Println("Integer", reader.GetField("Integer").Int())
250+
fmt.Println("Text", reader.GetField("Text").String())
251+
fmt.Println("Float", reader.GetField("Float").Float64())
252+
fmt.Println("Boolean", reader.GetField("Boolean").Bool())
253+
fmt.Println("Slice", reader.GetField("Slice").Interface().([]int))
254+
fmt.Println("Anonymous", reader.GetField("Anonymous").String())
255+
256+
var dataOne DataOne
257+
err = reader.ToStruct(&dataOne)
258+
fmt.Println(err, dataOne)
247259

260+
var dataTwo DataTwo
261+
err = reader.ToStruct(&dataTwo)
262+
fmt.Println(err, dataTwo)
248263
// Out:
249264
// Integer 123
250265
// Text example
251266
// Float 123.45
252267
// Boolean true
253268
// Slice [1 2 3]
254269
// Anonymous
270+
// <nil> {123 example 123.45}
271+
// <nil> {true [1 2 3] }
255272
}
256273
```

reader.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package dynamicstruct
22

33
import (
4+
"errors"
45
"fmt"
56
"reflect"
67
"time"
@@ -27,6 +28,8 @@ type (
2728
// for _, field := range reader.GetAllFields() { ...
2829
//
2930
GetAllFields() []Field
31+
32+
ToStruct(value interface{}) error
3033
}
3134

3235
// Field is a wrapper for struct's field's value.
@@ -278,6 +281,56 @@ func (r readImpl) GetAllFields() []Field {
278281
return fields
279282
}
280283

284+
func (r readImpl) ToStruct(value interface{}) error {
285+
valueOf := reflect.ValueOf(value)
286+
287+
if valueOf.Kind() != reflect.Ptr || valueOf.IsNil() {
288+
return errors.New("ToStruct: expected a pointer as an argument")
289+
}
290+
291+
valueOf = valueOf.Elem()
292+
typeOf := valueOf.Type()
293+
294+
if valueOf.Kind() != reflect.Struct {
295+
return errors.New("ToStruct: expected a pointer to struct as an argument")
296+
}
297+
298+
for i := 0; i < valueOf.NumField(); i++ {
299+
fieldType := typeOf.Field(i)
300+
fieldValue := valueOf.Field(i)
301+
302+
original, ok := r.fields[fieldType.Name]
303+
if !ok {
304+
continue
305+
}
306+
307+
if fieldValue.CanSet() && r.haveSameTypes(original.value.Type(), fieldValue.Type()) {
308+
fieldValue.Set(original.value)
309+
}
310+
}
311+
312+
return nil
313+
}
314+
315+
func (r readImpl) haveSameTypes(first reflect.Type, second reflect.Type) bool {
316+
if first.Kind() != second.Kind() {
317+
return false
318+
}
319+
320+
switch first.Kind() {
321+
case reflect.Ptr:
322+
return r.haveSameTypes(first.Elem(), second.Elem())
323+
case reflect.Struct:
324+
return first.PkgPath() == second.PkgPath() && first.Name() == second.Name()
325+
case reflect.Slice:
326+
return r.haveSameTypes(first.Elem(), second.Elem())
327+
case reflect.Map:
328+
return r.haveSameTypes(first.Elem(), second.Elem()) && r.haveSameTypes(first.Key(), second.Key())
329+
default:
330+
return first.Kind() == second.Kind()
331+
}
332+
}
333+
281334
func (f fieldImpl) Name() string {
282335
return f.field.Name
283336
}

0 commit comments

Comments
 (0)