我的應用程式采用 JSON 字串,將其解組為 struct BiggerType,然后使用mapstructure將 struct 的欄位解碼Settings為 type MainType。BiggerType需要支持多種型別,Settings因此, is 并且必須宣告為map[string]interface{}。該應用程式過去一直運行良好,直到我們有了一種新型別Settings,即MainType包含一些型別為 的自定義欄位SpecialType。
結構和主要代碼包括在下面。運行代碼會出現以下錯誤。
* 'b' expected a map, got 'string'
為簡潔起見,洗掉了一些代碼
package main
import (
...
"github.com/mitchellh/mapstructure"
)
const myJSON = `{
"settings": {
"a": {
"aa": {
"aaa": {
"sta": "special_object_A",
"stb": "special_object_B"
},
"aab": "bab"
},
"ab": true
},
"b": "special_string"
},
"other": "other"
}`
func main() {
var biggerType BiggerType
err := json.Unmarshal([]byte(myJSON), &biggerType)
if err != nil {
panic(err)
}
var decodedMainType MainType
if err := mapstructure.Decode(biggerType.Settings, &decodedMainType); err != nil {
panic(err)
}
}
type BiggerType struct {
Other string `json:"other"`
// Settings MainType `json:"settings"` can't be used as it needs to support other "Settings"
Settings map[string]interface{} `json:"settings"`
}
type A struct {
Aa *AA `json:"aa"`
Ab *bool `json:"ab"`
}
type AA struct {
Aaa SpecialType `json:"aaa"`
Aab string `json:"aab"`
}
type MainType struct {
A A `json:"a"`
B SpecialType `json:"b"`
}
type SpecialTypeObject struct {
Sta string `json:"sta"`
Stb string `json:"stb"`
}
func (s SpecialTypeObject) InterfaceMethod() (string, error) {
return s.Sta " " s.Stb, nil
}
type SpecialTypeString string
func (s SpecialTypeString) InterfaceMethod() (string, error) {
return string(s), nil
}
type SpecialInterface interface {
InterfaceMethod() (string, error)
}
// SpecialType SpecialTypeString | SpecialTypeObject
type SpecialType struct {
Value SpecialInterface
}
func (s *SpecialType) UnmarshalJSON(data []byte) error {
...
}
我的目標是能夠解碼biggerType.Settings成decodedMainType完整的所有值。任何人都可以與我分享任何解決方法或/和建議嗎?
復制問題的操場:https : //go.dev/play/p/G6mdnVoE2vZ
謝謝你。
uj5u.com熱心網友回復:
為什么它不起作用
* 'b' expected a map, got 'string'
在JSON中,settings.b是字串型別。您已將其解碼為 a BiggerType,其中設定為map[string]interface{}. 標準庫提供的 JSON Unmarshalling 程序會生成如下映射:
map[string]interface{} {
"A": map[string]interface{}{...},
"B": string("special_value"),
}
JSON 解碼現已完成。程式中不再發生 Json 解碼。
然后您嘗試使用mapstructure將您的解碼settings map成 a MainType,其中B型別為SettingsType。因此mapstructure.Decode盡職盡責地遍歷您的BiggerType并嘗試將其轉換為MainType,但遇到了障礙。當它到達時B,輸入是一個字串,輸出是一個SettingsType。
您UnmarshalJSON為設定型別撰寫了一個函式,但是B沒有從 JSON 中解組 's 的值 - 它已經是一個字串。解碼不知道如何將 astring變成 a SpecialType(它說它期望 a map但它有點誤導,因為它實際上檢查映射、結構、切片和陣列型別)
重新表述您面臨的問題
那么讓我們更具體地識別錯誤:你有。map[string]interface{}使用字串值,mapstructure.Decode嘗試將其解碼為具有 Struct 值的 struct 欄位。
重新定義目標
您在此處顯示的所有內容都是您在解決方案中面臨的問題。你的根本問題是什么?您有一個可能是 a也可能是 a的interface{}in ,并且您想將它變成包含 a或 a 的 aBiggerType.Settingsstringmap[string]interface{}SpecialTypeSpecialTypeStringSpecialTypeObj
我不能完全解決你的問題,你一直在談論MainType可能是不同的型別,但你程式中的所有不同型別似乎都在SpecialType.
怎么解決
似乎這里顯而易見的解決方案是放棄mapstructure并使用您已經用于SpecialType自定義解組器的方法。 mapstructure當您需要解組 JSON 以自省某些屬性然后決定如何處理其他屬性時,這很有用。但是,您并沒有這樣做mapstructure;最接近的是您的自定義解組器。如果自定義解組器可以完成這項作業,則您不需要mapstructure.
SpecialType.UnmarshalJSON在您的代碼中已經是如何將未知值包裝在已知型別中并使用switch陳述句來處理未知值型別的示例。
所以只是放棄mapstructure,重新定義BiggerType為:
type BiggerType struct {
Other string `json:"other"`
Settings MainType `json:"settings"`
}
并讓已經寫好的UnmarshalJSON句柄MainType.B.
如果不是SpecialType需要這樣的邏輯,但是MainType,然后做同樣的事情SpecialType,你沒有來MainType。
https://go.dev/play/p/DW8N8t3Lr0l
如果您想查看帶有 的解決方案mapstructure,請提供多個 JSON 輸入示例,以便我了解它們的不同之處以及mapstructure可能需要的原因。
uj5u.com熱心網友回復:
我和 Daniel Farrell 一起討論這個問題,但可以修復現有的解決方案。
這個想法是利用 的mapstructure定制它的能力,它Decoder可以在解碼程序中的關鍵點呼叫一個“鉤子”。有多個可用的鉤子,它們可以組合成一個“鏈式”鉤子。
由于有一個可以檢查目標變數的型別是否實作的股票掛鉤encoding.TextUnmarshaler,我們可以調整您(*SpecialType).UnmarshalJSON的成為(*SpecialType).UnmarshalText,然后使用它:
func (s *SpecialType) UnmarshalText(data []byte) error {
if len(data) > 0 && data[0] != '{' {
s.Value = SpecialTypeString(data)
return nil
}
var obj SpecialTypeObject
if err := json.Unmarshal(data, &obj); err != nil {
return fmt.Errorf("failed to unmarshal SpecialType: %s", err)
}
s.Value = obj
return nil
}
和
var decodedMainType MainType
msd, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
DecodeHook: mapstructure.TextUnmarshallerHookFunc(),
Result: &decodedMainType,
})
if err != nil {
return BiggerType{}, err
}
if err := msd.Decode(biggerType.Settings); err != nil {
return BiggerType{}, err
}
完整的解決方案:https : //go.dev/play/p/jaCD9FdSECz。
但話又說回來——正如丹尼爾暗示的那樣,你似乎已經愛上了使用,mapstructure因為它承諾是一個看起來很復雜的問題的簡單解決方案。
事實上,encoding/json似乎有庫存??設施來覆寫你的用例——正是通過使某些型別實作json.Unmarshaler,所以我試圖放棄mapstructure這種情況:它是一個有用的包,但現在你似乎在解決框架的規則它執行。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/386268.html
