2 回答

TA貢獻1820條經(jīng)驗 獲得超9個贊
您的類型與您擁有的 JSON 不匹配:
type Status struct {
Status bool
}
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Status map[string]Status `json:"status"`
}
映射到 JSON,看起來像這樣:
{
"name": "foo",
"age": 12,
"status": {
"some-string": {
"Status": true
}
}
}
在 go 類型中使用已知/未知字段的組合來解組數(shù)據(jù)的最簡單方法是使用如下內(nèi)容:
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Random map[string]interface{} `json:"-"` // skip this key
}
然后,首先解組已知數(shù)據(jù):
var p Person
if err := json.Unmarshal([]byte(jsonStream), &p); err != nil {
panic(err)
}
// then unmarshal the rest of the data
if err := json.Unmarshal([]byte(jsonStream), &p.Random); err != nil {
panic(err)
}
現(xiàn)在Random地圖將包含所有數(shù)據(jù),包括name和age字段。看到你已經(jīng)在結(jié)構(gòu)上標記了那些,這些鍵是已知的,所以你可以輕松地從地圖中刪除它們:
delete(p.Random, "name")
delete(p.Random, "age")
現(xiàn)在p.Random將包含所有未知鍵及其各自的值。這些值顯然將是一個帶有 field 的對象status,它應(yīng)該是一個布爾值。您可以著手使用類型斷言并將它們?nèi)哭D(zhuǎn)換為更合理的類型,或者您可以采取快捷方式并編組/取消編組值。像這樣更新你的Person類型:
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Random map[string]interface{} `json:"-"`
Statuses map[string]Status `json:"-"`
}
現(xiàn)在獲取干凈的Random值,將其編組并將其解組回Statuses字段中:
b, err := json.Marshal(p.Random)
if err != nil {
panic(err)
}
if err := json.Unmarshal(b, &p.Statuses); err != nil {
panic(err)
}
// remove Random map
p.Random = nil
結(jié)果Person.Statuses["bvu62fu6dq"].Status設(shè)置為true
清理這一切,并將數(shù)據(jù)編組回來
現(xiàn)在,因為我們的Random和Statuses字段被標記為在 JSON 編組 ( json:"-") 中被忽略,當您想從這些類型輸出原始 JSON 時,編組這種Person類型不會很好。最好將此邏輯包裝在自定義 JSON (un)-Marshaller 接口中。您可以在類型的MarshalJSON和UnmarshalJSON方法中使用一些中間類型Person,或者只創(chuàng)建一個映射并設(shè)置您需要的鍵:
func (p Person) MarshalJSON() ([]byte, error) {
data := make(map[string]interface{}, len(p.Statuses) + 2) // 2 being the extra fields
// copy status fields
for k, v := range p.Statuses {
data[k] = v
}
// add known keys
data["name"] = p.Name
data["age"] = p.Age
return json.Marshal(data) // return the marshalled map
}
同樣,您可以為 做同樣的事情UnmarshalJSON,但您需要創(chuàng)建一個Person沒有自定義處理的類型版本:
type intermediaryPerson struct {
Name string `json:"name"`
Age int `json:"age"`
Random map[string]interface{} `json:"-"`
}
// no need for the tags and helper fields anymore
type Person struct {
Name string
Age int
Statuses map[string]Status // Status type doesn't change
}
func (p *Person) UnmarshalJSON(data []byte) error {
i := intermediaryPerson{}
if err := json.Unmarshal(data, &i); err != nil {
return err
}
if err := json.Unmarshal(data, &i.Random); err != nil {
return err
}
delete(i.Random, "name")
delete(i.Random, "age")
stat, err := json.Marshal(i.Random)
if err != nil {
return err
}
// copy known fields
p.Name = i.Name
p.Age = i.Age
return json.Unmarshal(stat, &p.Statuses) // set status fields
}
在這種情況下,創(chuàng)建一個處理已知字段的類型并將其嵌入是很常見的,不過:
type BasePerson struct {
Name string `json:"name"`
Age int `json:"age"`
}
并將其嵌入中介和“主要”/導出類型中:
type interPerson struct {
BasePerson
Random map[string]interface{} `json:"-"`
}
type Person struct {
BasePerson
Statuses map[string]Status
}
這樣,您可以直接將已知字段解組為BasePerson類型,分配它,然后處理映射:
func (p *Person) UnmarshalJSON(data []byte) error {
base := BasePerson{}
if err := json.Unmarshal(data, &base); err != nil {
return err
}
p.BasePerson = base // takes care of all known fields
unknown := map[string]interface{}{}
if err := json.Unmarshal(data, unknown); err != nil {
return err
}
// handle status stuff same as before
delete(unknown, "name") // remove known fields
// marshal unknown key map, then unmarshal into p.Statuses
}
演示 2
這就是我的處理方式。它允許調(diào)用json.Marshal和json.Unmarshal看起來就像任何其他類型,它將未知字段的處理集中在一個地方(編組器/解組器接口的實現(xiàn)),并為您留下一個Person類型,其中每個字段都包含所需的數(shù)據(jù), 以可用的格式。它的效率有點低,因為它依賴于對未知密鑰的解組/編組/解組。你可以取消它,就像我說的那樣,使用類型斷言并在地圖上迭代,unknown而是用這樣的東西來解決:
for k, v := range unknown {
m, ok := v.(map[string]interface{})
if !ok {
continue // not {"status": bool}
}
s, ok := m["status"]
if !ok {
continue // status key did not exist, ignore
}
if sb, ok := s.(bool); ok {
// ok, we have a status bool value
p.Statuses[k] = Status{
Status: sb,
}
}
}
但說實話,性能差異不會那么大(這是微優(yōu)化 IMO),而且代碼對我來說有點太冗長了。懶惰,在需要時優(yōu)化,而不是隨時優(yōu)化

TA貢獻1875條經(jīng)驗 獲得超3個贊
類型不符合您的 json 值。
const jsonStream = `{
"name": "john",
"age": 23,
"bvu62fu6dq": {
"status": true
}
}`
對于上面的 json,您的代碼應(yīng)該像下面的 snnipet 一樣工作(對現(xiàn)有代碼進行一些修改)。
package main
import (
"encoding/json"
"fmt"
"io"
"log"
"strings"
)
const jsonStream = `{
"name": "john",
"age": 23,
"bvu62fu6dq": {
"status": true
}
}`
type bvu62fu6dq struct {
Status bool
}
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Status bvu62fu6dq `json:"bvu62fu6dq"`
}
func main() {
dec := json.NewDecoder(strings.NewReader(jsonStream))
for {
var person Person
if err := dec.Decode(&person); err == io.EOF {
break
} else if err != nil {
log.Fatal(err)
}
fmt.Println(person)
fmt.Println(person.Status)
}
}
根據(jù)您的 json 數(shù)據(jù),您必須使用類型字段進行映射。 運行代碼片段
- 2 回答
- 0 關(guān)注
- 125 瀏覽
添加回答
舉報