JSON Unmarshal не работает с динамическими c структурами, созданными с помощью relect - PullRequest
0 голосов
/ 08 мая 2020

Я пытаюсь разобрать JSON файлов, используя динамически созданные структуры, но, видимо, что-то делаю не так. Может кто-нибудь, пожалуйста, скажите нам, что я здесь делаю не так:

structured := make(map[string][]reflect.StructField)
structured["Amqp1"] = []reflect.StructField{
    reflect.StructField{
        Name: "Test",
        Type: reflect.TypeOf(""),
        Tag:  reflect.StructTag(`json:"test"`),
    },
    reflect.StructField{
        Name: "Float",
        Type: reflect.TypeOf(5.5),
        Tag:  reflect.StructTag(`json:"float"`),
    },
    reflect.StructField{
        Name: "Connections",
        Type: reflect.TypeOf([]Connection{}),
        Tag:  reflect.StructTag(`json:"connections"`),
    },
}

sections := []reflect.StructField{}
for sect, params := range structured {
    sections = append(sections,
        reflect.StructField{
            Name: sect,
            Type: reflect.StructOf(params),
        },
    )
}

parsed := reflect.New(reflect.StructOf(sections)).Elem()
if err := json.Unmarshal([]byte(JSONConfigContent), &parsed); err != nil {
    fmt.Printf("unable to parse data from provided configuration file: %s\n", err)
    os.Exit(1)
}

https://play.golang.org/p/C2I4Pduduyg

Заранее спасибо.

Ответы [ 3 ]

1 голос
/ 08 мая 2020

Вы хотите использовать .Interface() для возврата фактического базового значения, которое должно быть указателем на конкретную анонимную структуру.

Обратите внимание, что функция reflect.New возвращает reflect.Value , представляющее a указатель на новое нулевое значение для указанного типа. В этом случае метод Interface возвращает указатель как interface{}, что является всем, что вам нужно для json.Unmarshal.

Если после демаршалинга вам нужен не-указатель на структуру, которую вы можете повернуть, чтобы снова отразить и использовать reflect.ValueOf(parsed).Elem().Interface() для эффективного разыменования указателя.

parsed := reflect.New(reflect.StructOf(sections)).Interface()
if err := json.Unmarshal([]byte(JSONConfigContent), parsed); err != nil {
    fmt.Printf("unable to parse data from provided configuration file: %s\n", err)
    os.Exit(1)
}

https://play.golang.org/p/Bzu1hUyKjvM

0 голосов
/ 08 мая 2020

Я бы порекомендовал что-то совсем другое. Я обычно по возможности избегаю размышлений. Вы можете выполнить sh то, что вы пытаетесь сделать, просто предоставив структуры для каждого типа конфигурации, которую вы ожидаете, а затем выполните начальную «предварительную демаршализацию», чтобы определить, какой тип конфигурации вы действительно должны использовать по имени (что в данном case - это ключ вашего объекта JSON):

package main

import (
    "encoding/json"
    "fmt"
    "os"
)

//Amqp1 config struct
type Amqp1 struct {
    Config struct {
        Test        string       `json:"test"`
        Float       float64      `json:"float"`
        Connections []Connection `json:"connections"`
    } `json:"Amqp1"`
}

//Connection struct
type Connection struct {
    Type string `json:"type"`
    URL  string `json:"url"`
}

//JSONConfigContent json
const JSONConfigContent = `{
    "Amqp1": {
        "test": "woobalooba",
        "float": 5.5,
        "connections": [
            {"type": "test1", "url": "booyaka"},
            {"type": "test2", "url": "foobar"}
        ]
    }
}`

func main() {
    configMap := make(map[string]interface{})
    if err := json.Unmarshal([]byte(JSONConfigContent), &configMap); err != nil {
        fmt.Printf("unable to parse data from provided configuration file: %s\n", err)
        os.Exit(1)
    }

    //get config name
    var configName string
    for cfg := range configMap {
        configName = cfg
        break
    }

    //unmarshal appropriately
    switch configName {
    case "Amqp1":
        var amqp1 Amqp1
        if err := json.Unmarshal([]byte(JSONConfigContent), &amqp1); err != nil {
            fmt.Printf("unable to parse data from provided configuration file: %s\n", err)
            os.Exit(1)
        }
        fmt.Printf("%s >>\n", configName)
        fmt.Printf("Test: %s\n", amqp1.Config.Test)
        fmt.Printf("Float: %v\n", amqp1.Config.Float)
        fmt.Printf("Connections: %#v\n", amqp1.Config.Connections)

    default:
        fmt.Printf("unknown config encountered: %s\n", configName)
        os.Exit(1)
    }
}

Первоначальный «предварительный демаршаллинг» происходит во второй строке main () на простой карте, где ключом является строка, а value is interface {}, потому что вам все равно. Вы просто делаете это, чтобы получить реальный тип конфигурации, которая является ключом первого вложенного объекта.

Вы также можете запустить это на игровой площадке .

0 голосов
/ 08 мая 2020

reflection.New возвращает значение, представляющее указатель. Изменить строку 69:

fmt.Printf(">>> %v", &parsed)

Результат:

>>> <struct { Amqp1 struct { Test string "json:\"test\""; Float float64 "json:\"float\""; Connections []main.Connection "json:\"connections\"" } } Value>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...