Skip to content

Instantly share code, notes, and snippets.

@super-dog-human
Last active July 31, 2021 12:23
Show Gist options
  • Save super-dog-human/f5d80d44de8f70e8dc17c38d43889cf9 to your computer and use it in GitHub Desktop.
Save super-dog-human/f5d80d44de8f70e8dc17c38d43889cf9 to your computer and use it in GitHub Desktop.
package domain
import (
"encoding/json"
"reflect"
"strings"
)
func MergeJsonToStruct(jsonDiff *map[string]interface{}, origin interface{}, allowFields *[]string) {
allKeys := TopLevelStructKeys(origin)
o := reflect.ValueOf(origin).Elem()
if !o.CanSet() {
return
}
for rawName, jsonValue := range *jsonDiff {
fieldName := strings.Title(rawName) // jsonのフィールド名を比較用にアッパーキャメルにする
if !Contains(&allKeys, fieldName) || !Contains(allowFields, fieldName) {
continue
}
jsonFieldType := reflect.TypeOf(jsonValue).String()
targetField := o.FieldByName(fieldName)
if jsonFieldType == "map[string]interface {}" {
childJson := jsonValue.(map[string]interface{})
switch childTarget := targetField.Interface().(type) {
case VoiceSynthesisConfig:
allowChildFields := TopLevelStructKeys(&childTarget) // 親フィールドが既に許可されているので子は全て許可
MergeJsonToStruct(&childJson, &childTarget, &allowChildFields)
targetField.Set(reflect.ValueOf(&childTarget).Elem())
}
} else if jsonFieldType == "[]interface {}" {
switch targets := targetField.Interface().(type) {
case []LessonReference:
var targetBlankStruct LessonReference
allowChildFields := TopLevelStructKeys(&targetBlankStruct)
for _, v := range jsonValue.([]interface{}) {
child := v.(map[string]interface{})
MergeJsonToStruct(&child, &targetBlankStruct, &allowChildFields)
targets = append(targets, targetBlankStruct)
}
targetField.Set(reflect.ValueOf(&targets).Elem()) // 配列は元の値にマージせず丸ごと置き換える
}
} else {
setValueToField(jsonValue, targetField)
}
}
}
func setValueToField(jsonValue interface{}, targetField reflect.Value) {
originFieldType := targetField.Type().Kind()
switch originFieldType {
case reflect.Bool:
targetField.SetBool(jsonValue.(bool))
case reflect.Float32, reflect.Float64:
targetField.SetFloat(jsonValue.(float64))
case reflect.Int32, reflect.Int64:
v := jsonValue.(float64) // jsonのintは全てfloatとして扱われるのでキャストする
targetField.SetInt(int64(v))
case reflect.String:
targetField.SetString(jsonValue.(string))
case reflect.Uint64:
targetField.SetUint(jsonValue.(uint64))
default:
// 独自にUnmarshallでenumを定義した型の場合
customField := reflect.New(targetField.Type()).Interface()
bytes := []byte("\"" + jsonValue.(string) + "\"")
json.Unmarshal(bytes, &customField)
targetField.Set(reflect.ValueOf(customField).Elem())
}
}
func TopLevelStructKeys(target interface{}) []string {
v := reflect.ValueOf(target).Elem()
var keys []string
for i := 0; i < v.NumField(); i++ {
keys = append(keys, v.Type().Field(i).Name)
}
return keys
}
func Contains(s *[]string, e string) bool {
for _, a := range *s {
if a == e {
return true
}
}
return false
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment