Предположим, у нас есть файл конфигурации вида: (я использую json)
Это короткий синтаксис
{
"network": "test-network"
}
И длинный синтаксис
{
"network": {
"name": "test-network",
"description": "for testing purposes"
}
}
При чтении этих конфигураций мы должны предоставить собственную функцию декодера для разбора и записи в наш объект.
Прежде чем это сделать, давайте проверим наш пример.
Сначала создадим struct для длинного и короткого формата и попробуем разобрать.
package main
import (
"encoding/json"
"fmt"
)
type NetworkDescription struct {
Name string `json:"name"`
Description string `json:"description"`
}
type NetworkLong struct {
Network NetworkDescription `json:"network"`
}
type NetworkShort struct {
Network string `json:"network"`
}
func main() {
networkLongTest1 := &NetworkLong{}
err := json.Unmarshal([]byte(`{"network":{"name":"foo","description":"bar"}}`), networkLongTest1)
fmt.Printf("%#vnErr: %v", networkLongTest1, err)
networkLongTest2 := &NetworkLong{}
err = json.Unmarshal([]byte(`{"network":"foo"}`), networkLongTest2)
fmt.Printf("%#vnErr: %v", networkLongTest2, err)
}
Результат, очевидно, дает ошибку во второй попытке
$ go run main.go
&main.NetworkLong{Network:main.NetworkDescription{Name:"foo", Description:"bar"}}
Err: <nil>
&main.NetworkLong{Network:main.NetworkDescription{Name:"", Description:""}}
Err: json: cannot unmarshal string into Go struct field NetworkLong.network of type main.NetworkDescription
Хорошо, теперь мы можем использовать эту ошибку в реализации интерфейса нашего пользовательского Unmarshaler’а.
Я использую json
из stdlib для распаковки, поэтому интерфейс Unmarshaler имеет одну функцию UnmarshalJSON([]byte) error
.
Мы можем создать новый тип struct и использовать его, но я просто изменю нашу структуру NetworkLong
, потому что я буду превращать короткую версию в длинную.
type NetworkLong struct {
Network NetworkDescription `json:"network"`
}
func (n *NetworkLong) UnmarshalJSON(b []byte) error {
// change type to don't use our custom unmarshal
type longFormat NetworkLong
nLong := &longFormat{}
err := json.Unmarshal(b, &nLong)
if err != nil {
nShort := &NetworkShort{}
err = json.Unmarshal(b, nShort)
if err != nil {
return err
}
n.Network = NetworkDescription{
Name: nShort.Network,
}
return nil
}
*n = NetworkLong(*nLong)
return nil
}
И наш результат
&main.NetworkLong{Network:main.NetworkDescription{Name:"foo", Description:"bar"}}
Err: <nil>
&main.NetworkLong{Network:main.NetworkDescription{Name:"foo", Description:""}}
Err: <nil>
Проверка на игровой площадке