これは、リフレクション、安全でない、または構造体ごとの関数のないソリューションです。この例は少し複雑で、このようにする必要はないかもしれませんが、重要なのは、map [string]interface{}を使用してフィールドタグを持つ構造体から離れることです。同様のソリューションでアイデアを使用できる可能性があります。
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":false}
{"num_bits":8}
編集:より多くの説明。トムがコメントで指摘しているように、コードの背後で反省が起こっています。ここでの目標は、ライブラリの利用可能な機能を使用してコードを単純に保つことです。パッケージjsonは現在、データを操作する2つの方法、構造体タグと[string]interface{}のマップを提供しています。structタグを使用するとフィールドを選択できますが、静的に1つのjsonフィールド名を選択する必要があります。マップでは、実行時にフィールド名を選択できますが、マーシャルするフィールドは選択できません。jsonパッケージで両方を同時に実行できると便利ですが、そうではありません。ここでの答えは、2つの手法と、OPの問題例のソリューションでそれらをどのように構成できるかを示しています。