简体   繁体   English

我该怎么做呢? 获取结构以接口的形式发送到func并将其作为结构返回?

[英]How do I do this? Get an struct send to a func as interface and get it back as struct?

I'm new in golang development and have some question regarding something related to this question . 我是golang开发的新手,对与此问题相关的问题有一些疑问

As a learning exercise, I'm trying to create a simple library to handle json based configuration file. 作为一项学习练习,我试图创建一个简单的库来处理基于json的配置文件。 As a configuration file to be used for more then one app, it should be able to handle different parameters. 作为用于一个以上应用程序的配置文件,它应该能够处理不同的参数。 Then I have created a type struct Configuration that has the filename and a data interface. 然后,我创建了一个具有文件名和数据接口的类型struct Configuration。 Each app will have a struct based on its configuration needs. 每个应用程序都将根据其配置需求具有结构。

In the code bellow, I put all together (lib and "main code") and the "TestData struct" is the "app parameters". 在下面的代码中,我将所有内容放在一起(lib和“主代码”),“ TestData结构”是“应用程序参数”。 If it doesn't exists, it will set a default values and create the file, and it is working. 如果不存在,它将设置默认值并创建文件,并且文件正在运行。 But when I try to read the file. 但是当我尝试读取文件时。 I try to decode the json and put it back into the data interface. 我尝试解码json并将其放回数据接口。 But it is giving me an error and I couldn't figure out how to solve this. 但这给了我一个错误,我不知道该如何解决。 Can someone help on this? 有人可以帮忙吗?

[updated] I didn't put the targeted code before, because I though that it would be easier to read in in all as a single program. [更新]我之前没有输入目标代码,因为我虽然可以将它作为一个单独的程序轻松阅读。 Bellow is the 'targeted code' for better view of the issue. 为了更好地了解问题,贝娄是“目标代码”。 As I will not be able to use the TestData struct inside the library, since it will change from program to program, the only way to handle this was using interface. 由于我将无法在库中使用TestData结构,因为它会因程序而异,所以处理此问题的唯一方法是使用接口。 Is there a better way? 有没有更好的办法?

library config 库配置

package config

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

// Base configuration struct
type Configuration struct {
    Filename string
    Data     interface{}
}

func (c *Configuration) Create(cData *Configuration) bool {
    cFile, err := os.Open(cData.Filename)
    defer cFile.Close()
    if err == nil {
        fmt.Println("Error(1) trying to create a configuration file. File '", cData.Filename, "' may already exist...")
        return false
    }
    cFile, err = os.Create(cData.Filename)
    if err != nil {
        fmt.Println("Error(2) trying to create a configuration file. File '", cData.Filename, "' may already exist...")
        return false
    }
    buffer, _ := json.MarshalIndent(cData.Data, "", "")
    cFile.Write(buffer)
    return true
}

func (c *Configuration) Read(cData *Configuration) bool {
    cFile, err := os.Open(cData.Filename)
    defer cFile.Close()
    if err != nil {
        fmt.Println("Error(1) trying to read a configuration file. File '", cData.Filename, "' may not already exist...")
        return false
    }
    jConfig := json.NewDecoder(cFile)
    jerr := jConfig.Decode(&cData.Data)
    if jerr != nil {
        panic(jerr)
    }
    return true
}

program using library config 使用库配置程序

package main

import (
    "fmt"

    "./config"
)

// struct basic para configuração
type TestData struct {
    URL  string
    Port string
}

func main() {
    var Config config.Configuration
    Config.Filename = "config.json"

    if !Config.Read(&Config) {
        Config.Data = TestData{"http", "8080"}
        Config.Create(&Config)
    }
    fmt.Println(Config.Data)
    TestData1 := &TestData{}
    TestData1 = Config.Data.(*TestData) // error, why?
    fmt.Println(TestData1.URL)
}

NEW UPDATE: I have made some changes after JimB comment about I'm not clear about some concepts and I tried to review it. 新更新:在JimB评论我不清楚某些概念并尝试对其进行回顾之后,我进行了一些更改。 Sure many things aren't clear for me yet unfortunately. 不幸的是,很多事情对我来说还不清楚。 The "big" understanding I believe I got, but what mess my mind up is the "ins" and "outs" of values and formats and pointers, mainly when it goes to other libraries. 我相信我获得了“大的”理解,但是让我烦恼的是值,格式和指针的“输入”和“输出”,主要是当它进入其他库时。 I'm not able yet to follow the "full path" of it. 我还无法遵循它的“完整路径”。

Yet, I believe I had some improvement on my code . 但是,我相信自己的代码有所改进。

I think that I have corrected some points, but still have some big questions: 我认为我已经纠正了一些观点,但是仍然存在一些大问题:

  1. I stopped sending "Configuration" as a parameter as all "data" were already there as they are "thenselfs" in the instance. 我停止发送“配置”作为参数,因为所有“数据”已经存在,因为它们是实例中的“自己”。 Right? 对?
  2. Why do I have use reference in the line 58 (Config.Data = &TestData{}) 为什么在第58行中使用引用(Config.Data =&TestData {})
  3. Why to I have to use pointer in the line 64 (tmp := Config.Data.(*TestData) 为什么我必须在第64行中使用指针(tmp:= Config.Data。(* TestData)
  4. Why I CANNOT use reference in line 69 (Config.Data = tmp) 为什么我不能在第69行中使用引用(Config.Data = tmp)

Thanks 谢谢

You are trying to assert that Config.Data is of type *TestData , but you're assigning it to TestData{"http", "8080"} above. 您试图断言Config.Data的类型为*TestData ,但是您正在将其分配给上面的TestData{"http", "8080"} You can take the address of a composite literal to create a pointer: 您可以使用复合文字的地址来创建指针:

Config.Data = &TestData{"http", "8080"}

If your config already exsits, your Read method is going to fill in the Data field with the a default json data type, probably a map[string]interface{} . 如果您的配置已经存在,则您的Read方法将使用默认的json数据类型(可能是map[string]interface{}填充Data字段。 If you assign a pointer of the correct type to Data first, it will decode into the expected type. 如果您首先将正确类型的指针分配给Data ,则它将解码为所需类型。

Config.Data = &TestData{}

Ans since Data is an interface{} , you do not want to ever use a pointer to that value, so don't use the & operator when marshaling and unmarshaling. 回答:由于Datainterface{} ,因此您永远都不想使用指向该值的指针,因此在编组和解编组时不要使用&运算符。

The reason you are running into an error is because you are trying to decode into an interface{} type. 您遇到错误的原因是因为您尝试将其解码为interface{}类型。 When dealing with JSON objects, they are decoded by the encoding/json package into map[string]interface{} types by default. 在处理JSON对象时,默认情况下,它们会由encoding / json包解码为map[string]interface{}类型。 This is causing the type assertion to fail since the memory structure for a map[string]interface{} is much different than that of a struct. 这导致类型声明失败,因为map [string] interface {}的内存结构与结构的内存结构大不相同。

The better way to do this is to make your TestData struct the expected data format for your Configuration struct: 更好的方法是使TestData结构成为Configuration结构的预期数据格式:

// Base configuration struct
type Configuration struct {
    Filename string
    Data     *TestData
}

Then when Decoding the file data, the package will unmarshal the data into the fields that match the closest with the data it finds. 然后,在对文件数据进行解码时,程序包会将数据解组到与最接近的字段和找到的数据匹配的字段中。

If you need more control over the data unmarshaling process, you can dictate which JSON fields get decoded into which struct members by using struct tags. 如果您需要对数据解组过程进行更多控制,则可以使用struct标签指定将哪些JSON字段解码为哪些struct成员。 You can read more about the json struct tags available here: https://golang.org/pkg/encoding/json/#Marshal 您可以在此处阅读有关json struct标签的更多信息: https : //golang.org/pkg/encoding/json/#Marshal

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM