将map转换为struct

时间:2014-11-04 20:56:40

标签: go

我正在尝试在Go中创建一个通用方法,该方法将使用struct中的数据填充map[string]interface{}。例如,方法签名和用法可能如下所示:

func FillStruct(data map[string]interface{}, result interface{}) {
    ...
}

type MyStruct struct {
    Name string
    Age  int64
}

myData := make(map[string]interface{})
myData["Name"] = "Tony"
myData["Age"]  = 23

result := &MyStruct{}
FillStruct(myData, result)

// result now has Name set to "Tony" and Age set to 23

我知道这可以使用JSON作为中介来完成;还有另一种更有效的方法吗?

7 个答案:

答案 0 :(得分:65)

最简单的方法是使用https://github.com/mitchellh/mapstructure

import "github.com/mitchellh/mapstructure"

mapstructure.Decode(myData, &result)

如果你想自己做,你可以做这样的事情:

http://play.golang.org/p/tN8mxT_V9h

func SetField(obj interface{}, name string, value interface{}) error {
    structValue := reflect.ValueOf(obj).Elem()
    structFieldValue := structValue.FieldByName(name)

    if !structFieldValue.IsValid() {
        return fmt.Errorf("No such field: %s in obj", name)
    }

    if !structFieldValue.CanSet() {
        return fmt.Errorf("Cannot set %s field value", name)
    }

    structFieldType := structFieldValue.Type()
    val := reflect.ValueOf(value)
    if structFieldType != val.Type() {
        return errors.New("Provided value type didn't match obj field type")
    }

    structFieldValue.Set(val)
    return nil
}

type MyStruct struct {
    Name string
    Age  int64
}

func (s *MyStruct) FillStruct(m map[string]interface{}) error {
    for k, v := range m {
        err := SetField(s, k, v)
        if err != nil {
            return err
        }
    }
    return nil
}

func main() {
    myData := make(map[string]interface{})
    myData["Name"] = "Tony"
    myData["Age"] = int64(23)

    result := &MyStruct{}
    err := result.FillStruct(myData)
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println(result)
}

答案 1 :(得分:60)

Hashicorp的https://github.com/mitchellh/mapstructure库开箱即用:

import "github.com/mitchellh/mapstructure"

mapstructure.Decode(myData, &result)

第二个result参数必须是结构的地址。

答案 2 :(得分:12)

你可以做到......它可能会变得有点丑陋,你会在映射类型方面面临一些试验和错误......但是它的基本要点是:

func FillStruct(data map[string]interface{}, result interface{}) {
    t := reflect.ValueOf(result).Elem()
    for k, v := range data {
        val := t.FieldByName(k)
        val.Set(reflect.ValueOf(v))
    }
}

工作样本:http://play.golang.org/p/PYHz63sbvL

答案 3 :(得分:2)

您可以通过 JSON 来回传输它:

package main

import (
   "bytes"
   "encoding/json"
)

func transcode(in, out interface{}) {
   buf := new(bytes.Buffer)
   json.NewEncoder(buf).Encode(in)
   json.NewDecoder(buf).Decode(out)
}

示例:

package main
import "fmt"

type myStruct struct {
   Name string
   Age  int64
}

func main() {
   myData := map[string]interface{}{
      "Name": "Tony",
      "Age": 23,
   }
   var result myStruct
   transcode(myData, &result)
   fmt.Printf("%+v\n", result) // {Name:Tony Age:23}
}

答案 4 :(得分:1)

我调整了dave的答案,并添加了一个递归功能。我还在开发一个更加用户友好的版本。例如,地图中的数字字符串应该能够在结构中转换为int。

package main

import (
    "fmt"
    "reflect"
)

func SetField(obj interface{}, name string, value interface{}) error {

    structValue := reflect.ValueOf(obj).Elem()
    fieldVal := structValue.FieldByName(name)

    if !fieldVal.IsValid() {
        return fmt.Errorf("No such field: %s in obj", name)
    }

    if !fieldVal.CanSet() {
        return fmt.Errorf("Cannot set %s field value", name)
    }

    val := reflect.ValueOf(value)

    if fieldVal.Type() != val.Type() {

        if m,ok := value.(map[string]interface{}); ok {

            // if field value is struct
            if fieldVal.Kind() == reflect.Struct {
                return FillStruct(m, fieldVal.Addr().Interface())
            }

            // if field value is a pointer to struct
            if fieldVal.Kind()==reflect.Ptr && fieldVal.Type().Elem().Kind() == reflect.Struct {
                if fieldVal.IsNil() {
                    fieldVal.Set(reflect.New(fieldVal.Type().Elem()))
                }
                // fmt.Printf("recursive: %v %v\n", m,fieldVal.Interface())
                return FillStruct(m, fieldVal.Interface())
            }

        }

        return fmt.Errorf("Provided value type didn't match obj field type")
    }

    fieldVal.Set(val)
    return nil

}

func FillStruct(m map[string]interface{}, s interface{}) error {
    for k, v := range m {
        err := SetField(s, k, v)
        if err != nil {
            return err
        }
    }
    return nil
}

type OtherStruct struct {
    Name string
    Age  int64
}


type MyStruct struct {
    Name string
    Age  int64
    OtherStruct *OtherStruct
}



func main() {
    myData := make(map[string]interface{})
    myData["Name"]        = "Tony"
    myData["Age"]         = int64(23)
    OtherStruct := make(map[string]interface{})
    myData["OtherStruct"] = OtherStruct
    OtherStruct["Name"]   = "roxma"
    OtherStruct["Age"]    = int64(23)

    result := &MyStruct{}
    err := FillStruct(myData,result)
    fmt.Println(err)
    fmt.Printf("%v %v\n",result,result.OtherStruct)
}

答案 5 :(得分:1)

有两个步骤:

  1. 将接口转换为JSON字节
  2. 将JSON字节转换为struct

下面是一个示例:

dbByte, _ := json.Marshal(dbContent)
_ = json.Unmarshal(dbByte, &MyStruct)

答案 6 :(得分:0)

举例

package com.example.aravi.connect3withconstraintlayout;
import android.media.Image;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.support.v7.widget.GridLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;



import java.util.Arrays;

public class MainActivity extends AppCompatActivity {
// 0 - yellow 1 = red
int a=0;
int[] b={2,2,2,2,2,2,2,2,2};
int[][] winningPositons={‌{0,3,6},{0,1,2},{3,4,5},{6,7,8},{1,4,7},{2,5,8}, 
{0,4,8},{2,4,6}};
// 2 - not reserved
boolean playerisActive = true;

public void dropIn(View aravind) {

ImageView pointer = (ImageView) aravind;

int pointer_tag = Integer.parseInt(pointer.getTag().toString());
if (b[pointer_tag] == 2 && playerisActive) {
b[pointer_tag] = a;
pointer.setTranslationY(-500f);
if (a == 0) {
        pointer.setImageResource(R.drawable.yellow);
        a = 1;
} else {
        pointer.setImageResource(R.drawable.red2);
        a = 0;
}
pointer.animate().translationYBy(500f).setDuration(100);
System.out.println(Arrays.toString(b));
for(int[] winningPosition : winningPositons){
if(b[winningPosition[0]]==b[winningPosition[1]] && 
b[winningPosition[1]] == b[winningPosition[2]]  &&
b[winningPosition[0]] != 2){
playerisActive = false;
String winner = "Yellow";
if(b[winningPosition[0]] == 1)
{
                winner = "Red";
}

TextView vind = (TextView) findViewById(R.id.winnerMessage);
vind.setText(winner + " has won");
LinearLayout ara = (LinearLayout) findViewById(R.id.linearLayout);
ara.setVisibility(View.VISIBLE);
}  else{
     boolean gameisOver = true;
            for(int i : b){
                if(i == 2) {
                    gameisOver = false;
                }

}

if(gameisOver){
TextView vind = (TextView) findViewById(R.id.winnerMessage);
vind.setText("It's a Draw");
LinearLayout ara = (LinearLayout) 
findViewById(R.id.linearLayout);
ara.setVisibility(View.VISIBLE);
}
}
}
}
}

public void playAgain(View button){
playerisActive = true;
LinearLayout ara = (LinearLayout) findViewById(R.id.linearLayout);
ara.setVisibility(View.INVISIBLE);
a=0;
for(int i=0;i<b.length;i++){
    b[i]=2;
}

GridLayout gridlayout = (GridLayout) findViewById(R.id.gridLayout);
for(int g=0;g<gridlayout.getChildCount();g++){
    ((ImageView)gridlayout.getChildAt(g)).setImageDrawable(null);
}

}

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
}
}