Чтение длинного и короткого синтаксиса конфигурации в Go

Предположим, у нас есть файл конфигурации вида: (я использую 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>
Вход в полноэкранный режим Выход из полноэкранного режима

Проверка на игровой площадке

Оцените статью
devanswers.ru
Добавить комментарий