Last active
July 31, 2021 12:23
-
-
Save super-dog-human/f5d80d44de8f70e8dc17c38d43889cf9 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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