0

我试图通过使用动态创建的结构来解析 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

提前致谢。

4

3 回答 3

1

你想用来.Interface()返回实际的底层值,它应该是一个指向具体匿名结构的指针。

请注意,该reflect.New函数返回一个reflect.Value 表示指向指定类型的新零值的指针。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

于 2020-05-08T04:59:22.333 回答
0

reflect.New 返回一个表示指针的值。更改第 69 行:

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

结果:

>>> <struct { Amqp1 struct { Test string "json:\"test\""; Float float64 "json:\"float\""; Connections []main.Connection "json:\"connections\"" } } Value>
于 2020-05-07T22:54:26.233 回答
0

我会推荐一些非常不同的东西。我通常会尽可能避免反思。您可以通过简单地为您期望的每种配置类型提供结构来完成您想要做的事情,然后进行初始“预解组”以确定您应该按名称实际使用的配置类型(在这种情况下是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() 的第二行,到一个普通的映射,其中键是字符串,值是 interface{},因为你不在乎。您这样做只是为了获取实际的配置类型,这是第一个嵌套对象的键。

你也可以在操场上运行它

于 2020-05-07T23:11:46.480 回答