中间人复制结构之间的所有公共字段(Go- Copy all common fields betwee

2019-07-30 11:29发布

我具有存储JSON的数据库,并提供一个外部API来由此通过的HTTP POST,在该数据库中的值是可以改变的服务器。 该数据库用于由不同的内部处理,并且这样有一个共同的命名方案。

客户看到的关键是不同的,但映射1:在数据库中的键1(有未曝光的键)。 例如:

这是在数据库中:

{ "bit_size": 8, "secret_key": false }

这就是呈现给客户:

{ "num_bits": 8 }

该API可相对于改变字段名称,但数据库总是有一致的钥匙。

我有一个名为域的结构相同,标志不同的JSON编码器:

type DB struct {
    NumBits int  `json:"bit_size"`
    Secret  bool `json:"secret_key"`
}
type User struct {
    NumBits int `json:"num_bits"`
}

我使用的encoding/json做元帅/解组。

reflect这种情况的正确的工具? 是否有更简单的方法,因为所有的按键都是一样的吗? 我在想某种memcpy (如果我保持在同一订单的用户字段)。

Answer 1:

下面是使用反射的解决方案。 你必须进一步发展它,如果你需要使用嵌入式结构域和这种更复杂的结构。

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

package main

import (
    "encoding/json"
    "fmt"
    "reflect"
)

type M map[string]interface{} // just an alias

var Record = []byte(`{ "bit_size": 8, "secret_key": false }`)

type DB struct {
    NumBits int  `json:"bit_size"`
    Secret  bool `json:"secret_key"`
}

type User struct {
    NumBits int `json:"num_bits"`
}

func main() {
    d := new(DB)
    e := json.Unmarshal(Record, d)
    if e != nil {
        panic(e)
    }
    m := mapFields(d)
    fmt.Println("Mapped fields: ", m)
    u := new(User)
    o := applyMap(u, m)
    fmt.Println("Applied map: ", o)
    j, e := json.Marshal(o)
    if e != nil {
        panic(e)
    }
    fmt.Println("Output JSON: ", string(j))
}

func applyMap(u *User, m M) M {
    t := reflect.TypeOf(u).Elem()
    o := make(M)
    for i := 0; i < t.NumField(); i++ {
        f := t.FieldByIndex([]int{i})
        // skip unexported fields
        if f.PkgPath != "" {
            continue
        }
        if x, ok := m[f.Name]; ok {
            k := f.Tag.Get("json")
            o[k] = x
        }
    }
    return o
}

func mapFields(x *DB) M {
    o := make(M)
    v := reflect.ValueOf(x).Elem()
    t := v.Type()
    for i := 0; i < v.NumField(); i++ {
        f := t.FieldByIndex([]int{i})
        // skip unexported fields
        if f.PkgPath != "" {
            continue
        }
        o[f.Name] = v.FieldByIndex([]int{i}).Interface()
    }
    return o
}


Answer 2:

buf := bytes.Buffer{}
err := gob.NewEncoder(&buf).Encode(&DbVar)
if err != nil {
    return err
}
u := User{}
err = gob.NewDecoder(&buf).Decode(&u)
if err != nil {
    return err
}


Answer 3:

无法嵌入结构是有益的吗?

package main

import (
    "fmt"
)

type DB struct {
    User
    Secret bool `json:"secret_key"`
}

type User struct {
    NumBits int `json:"num_bits"`
}

func main() {
    db := DB{User{10}, true}
    fmt.Printf("Hello, DB: %+v\n", db)
    fmt.Printf("Hello, DB.NumBits: %+v\n", db.NumBits)
    fmt.Printf("Hello, User: %+v\n", db.User)
}

http://play.golang.org/p/9s4bii3tQ2



Answer 4:

使用结构标签,下面将肯定是好的,

package main

import (
    "fmt"
    "log"

    "hacked/json"
)

var dbj = `{ "bit_size": 8, "secret_key": false }`

type User struct {
    NumBits int `json:"bit_size" api:"num_bits"`
}

func main() {
    fmt.Println(dbj)
    // unmarshal from full db record to User struct
    var u User
    if err := json.Unmarshal([]byte(dbj), &u); err != nil {
        log.Fatal(err)
    }
    // remarshal User struct using api field names 
    api, err := json.MarshalTag(u, "api")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(string(api))
}

添加MarshalTag只需要一个小补丁encode.go:

106c106,112
<       e := &encodeState{}
---
>       return MarshalTag(v, "json")
> }
> 
> // MarshalTag is like Marshal but marshalls fields with
> // the specified tag key instead of the default "json".
> func MarshalTag(v interface{}, tag string) ([]byte, error) {
>       e := &encodeState{tagKey: tag}
201a208
>       tagKey       string
328c335
<               for _, ef := range encodeFields(v.Type()) {
---
>               for _, ef := range encodeFields(v.Type(), e.tagKey) {
509c516
< func encodeFields(t reflect.Type) []encodeField {
---
> func encodeFields(t reflect.Type, tagKey string) []encodeField {
540c547
<               tv := f.Tag.Get("json")
---
>               tv := f.Tag.Get(tagKey)


Answer 5:

这里的无反射,不安全的解决方案,或每一个结构功能。 这个例子是一个有点令人费解,也许你不会需要做的就是这样的,但关键是使用地图[字符串]接口{}摆脱与现场的标签的结构了。 您可能能够使用办法是在一个类似的解决方案。

package main

import (
    "encoding/json"
    "fmt"
    "log"
)

// example full database record
var dbj = `{ "bit_size": 8, "secret_key": false }`

// User type has only the fields going to the API
type User struct {
    // tag still specifies internal name, not API name
    NumBits int `json:"bit_size"`
}

// mapping from internal field names to API field names.
// (you could have more than one mapping, or even construct this
// at run time)
var ApiField = map[string]string{
    // internal: API
    "bit_size": "num_bits",
    // ...
}

func main() {
    fmt.Println(dbj)
    // select user fields from full db record by unmarshalling
    var u User
    if err := json.Unmarshal([]byte(dbj), &u); err != nil {
        log.Fatal(err)
    }
    // remarshal from User struct back to json
    exportable, err := json.Marshal(u)
    if err != nil {
        log.Fatal(err)
    }
    // unmarshal into a map this time, to shrug field tags.
    type jmap map[string]interface{}
    mInternal := jmap{}
    if err := json.Unmarshal(exportable, &mInternal); err != nil {
        log.Fatal(err)
    }
    // translate field names
    mExportable := jmap{}
    for internalField, v := range mInternal {
        mExportable[ApiField[internalField]] = v
    }
    // marshal final result with API field names
    if exportable, err = json.Marshal(mExportable); err != nil {
        log.Fatal(err)
    }
    fmt.Println(string(exportable))
}

输出:

{ “BIT_SIZE”:8中, “SECRET_KEY”:假}
{ “num_bits”:8}

编辑:更多的解释。 当汤姆的评论指出,有反射回事后面的代码。 这里的目标是通过使用图书馆的可用能力,保持代码的简洁。 包装JSON目前提供两种方式处理数据,结构标签和[字符串]接口{}的映射工作。 该结构的标签让您选择字段,但强迫你挑静态单一的JSON字段名。 该地图可让你选择在运行时字段名称,而不是哪些字段元帅。 这将是很好,如果JSON包让你一次都做,但事实并非如此。 这里的答案恰恰说明了这两种技术以及它们如何能够在OP解决例题组成。



Answer 6:

“这反映了这个正确的工具?” 一个更好的问题可能是,“都是结构标签给合适的工具?” 而答案可能是否定的。

package main

import (
    "encoding/json"
    "fmt"
    "log"
)

var dbj = `{ "bit_size": 8, "secret_key": false }`

// translation from internal field name to api field name
type apiTrans struct {
    db, api string
}

var User = []apiTrans{
    {db: "bit_size", api: "num_bits"},
}

func main() {
    fmt.Println(dbj)
    type jmap map[string]interface{}
    // unmarshal full db record
    mdb := jmap{}
    if err := json.Unmarshal([]byte(dbj), &mdb); err != nil {
        log.Fatal(err)
    }
    // build result
    mres := jmap{}
    for _, t := range User {
        if v, ok := mdb[t.db]; ok {
            mres[t.api] = v
        }
    }
    // marshal result
    exportable, err := json.Marshal(mres)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(string(exportable))
}


Answer 7:

你可以施放结构,如果他们有相同的字段名和类型,有效地重新分配字段标识:

package main

import "encoding/json"

type DB struct {
    dbNumBits
    Secret bool `json:"secret_key"`
}

type dbNumBits struct {
    NumBits int `json:"bit_size"`
}

type User struct {
    NumBits int `json:"num_bits"`
}

var Record = []byte(`{ "bit_size": 8, "secret_key": false }`)

func main() {
    d := new(DB)
    e := json.Unmarshal(Record, d)
    if e != nil {
        panic(e)
    }

    var u User = User(d.dbNumBits)
    println(u.NumBits)
}

https://play.golang.org/p/uX-IIgL-rjc



文章来源: Go- Copy all common fields between structs