在Golang中动态解组Yaml [英] Dynamically Unmarshaling yaml in golang

查看:79
本文介绍了在Golang中动态解组Yaml的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

添加问题上下文

我正在为ArangoDB数据库制作迁移工具.不知道那是什么吗?检出 https://www.arangodb.com .现在像大多数迁移工具一样,我的需要创建集合,这些集合在逻辑上等效于SQL表.它需要创建或删除索引,集合,图形和其他数据库实体,以及执行任意的AQL(SQL,如ArangoDB的代数).

I'm making a migration tool for the ArangoDB database. Don't know what that is? Checkout https://www.arangodb.com. Now like most migration tools, mine needs to create Collections, which are logically equivalent to SQL Tables. It needs to create or remove indexes, collections, graphs, and other DB entities as well as execute arbitrary AQL (SQL like algebra for ArangoDB).

为此,用户创建了一系列迁移文件,每个文件都说明了该步骤的处理方法.因此,步骤1可能会说创建集合用户",而步骤67可能会说删除集合用户-因为我们重命名了它".我想使用YAML,因为ArangoDB团队明确要求我为什么不这样做.由于二进制分发不需要Java,因此Golang似乎比我现有的Java实现更好.

To support this the user creates a series of migration files, each saying what to do with that step. So step 1 might say "Create Collection Users" and step 67 might say "Delete Collection Users -- since we renamed it". I want to use YAML because I was explicitly asked by the ArangoDB team why I didn't. Golang seems like a better choice than my existing Java implementation due to the binary distribution not requiring Java.

所有这些,我有几种不同类型的结构存储在不同的yaml文件中.我可以通过读取yaml的第一行来判断哪个结构在哪个文件中.我想在读取文件时尽可能地保持干​​燥.

All that said, I have several different types of structs stored in different yaml files. I can tell which struct is in which file by reading the first line of the yaml. I want to be as DRY as possible with reading the file.

我试图用一种工厂方法从此代码中的文件创建正确的结构.我们可以假设pickT的所有实例都将返回Migration接口的有效实现.

I tried to have a factory method that creates the proper struct to from the file in this code. We can assume that all instances of pickT will return a valid implementation of the Migration interface.

func pickT(contents []byte) (Migration, error) {
    s := string(contents)
    switch {
    case collection.MatchString(s):
        return new(Collection), nil
    default:
        return nil, errors.New("Can't determine YAML type")
    }
}

func toStruct(childPath string) Migration {
    contents := open(childPath)

    t, err := pickT(contents)
    if err != nil {
        log.Fatal(err)
    }
    //c := Collection{}
    err = yaml.UnmarshalStrict(contents, &t)
    if err != nil {
        log.Fatal(err)
    }
    return t
}

此代码不起作用.Yaml排除了以下错误.

This code does not work. Yaml kicks out the error below.

--- FAIL: TestLoadFromPath (0.00s)
panic: reflect.Set: value of type map[interface {}]interface {} is not assignable to type main.Migration [recovered]
    panic: reflect.Set: value of type map[interface {}]interface {} is not assignable to type main.Migration [recovered]
    panic: reflect.Set: value of type map[interface {}]interface {} is not assignable to type main.Migration

goroutine 5 [running]:
testing.tRunner.func1(0xc4201060f0)
    /usr/local/go/src/testing/testing.go:711 +0x2d2
panic(0x68ade0, 0xc420010cf0)
    /usr/local/go/src/runtime/panic.go:491 +0x283
gopkg.in/yaml%2ev2.handleErr(0xc420057b18)
    /home/jdavenpo/go/src/gopkg.in/yaml.v2/yaml.go:164 +0x9f
panic(0x68ade0, 0xc420010cf0)
    /usr/local/go/src/runtime/panic.go:491 +0x283
reflect.Value.assignTo(0x69e740, 0xc42007d230, 0x15, 0x6f30e4, 0xb, 0x6a45a0, 0xc420010cd0, 0x69e740, 0xc42007d230, 0x15)
    /usr/local/go/src/reflect/value.go:2192 +0x3a6
reflect.Value.Set(0x6a45a0, 0xc420010cd0, 0x194, 0x69e740, 0xc42007d230, 0x15)
    /usr/local/go/src/reflect/value.go:1357 +0xa4
gopkg.in/yaml%2ev2.(*decoder).mapping(0xc420060900, 0xc4200644e0, 0x6a45a0, 0xc420010cd0, 0x194, 0x6a45a0)
    /home/jdavenpo/go/src/gopkg.in/yaml.v2/decode.go:522 +0x8e2
gopkg.in/yaml%2ev2.(*decoder).unmarshal(0xc420060900, 0xc4200644e0, 0x6a45a0, 0xc420010cd0, 0x194, 0xc4200644e0)
    /home/jdavenpo/go/src/gopkg.in/yaml.v2/decode.go:292 +0x136
gopkg.in/yaml%2ev2.(*decoder).document(0xc420060900, 0xc420064480, 0x6a45a0, 0xc420010cd0, 0x194, 0xc4200608c0)
    /home/jdavenpo/go/src/gopkg.in/yaml.v2/decode.go:304 +0x80
gopkg.in/yaml%2ev2.(*decoder).unmarshal(0xc420060900, 0xc420064480, 0x6a45a0, 0xc420010cd0, 0x194, 0x194)
    /home/jdavenpo/go/src/gopkg.in/yaml.v2/decode.go:280 +0x1d2
gopkg.in/yaml%2ev2.unmarshal(0xc42010c000, 0x3b, 0x23b, 0x67c4a0, 0xc420010cd0, 0x1, 0x0, 0x0)
    /home/jdavenpo/go/src/gopkg.in/yaml.v2/yaml.go:101 +0x289
gopkg.in/yaml%2ev2.UnmarshalStrict(0xc42010c000, 0x3b, 0x23b, 0x67c4a0, 0xc420010cd0, 0x0, 0x0)
    /home/jdavenpo/go/src/gopkg.in/yaml.v2/yaml.go:87 +0x58
github.com/deusdat/arangomigo.toStruct(0xc420014360, 0x26, 0x0, 0x0)
    /home/jdavenpo/go/src/github.com/deusdat/arangomigo/migrations.go:102 +0x295
github.com/deusdat/arangomigo.loadFrom(0x6f8059, 0x1a, 0x0, 0x0, 0x0)
    /home/jdavenpo/go/src/github.com/deusdat/arangomigo/migrations.go:67 +0x4d6
github.com/deusdat/arangomigo.TestLoadFromPath(0xc4201060f0)
    /home/jdavenpo/go/src/github.com/deusdat/arangomigo/migrations_test.go:11 +0x48
testing.tRunner(0xc4201060f0, 0x705950)
    /usr/local/go/src/testing/testing.go:746 +0xd0
created by testing.(*T).Run
    /usr/local/go/src/testing/testing.go:789 +0x2de

据我所知,Go类型至少在功能上擦除了该界面.有什么方法可以动态选择类型,然后让Yaml将其转换出来?我知道我可以通过在不同的功能(例如下面的功能)中重复一遍又一遍地重复相同的逻辑来手动完成此操作,但这似乎很糟糕.

To the best of my understanding, Go type erases the interface, at least functionally. Is there any way to dynamically pick the type and then have Yaml convert it out? I know that I can do this manually by repeating the same logic over and over in different functions like the one below, but this seems...terrible.

func fromCollectionYaml(content string) Migration {
    c := Collection{}
    err := yaml.Unmarshal(content, &c)
    if err != nil {
        log.Fatal(err)
    }
    return c
}

添加Collection结构和Yaml

Adds the Collection struct and Yaml

type: collection
Action: create
Name: Users
Journalsize: 10

type Collection struct {
    Name           string
    Action         Action
    ShardKeys      []string
    JournalSize    int
    NumberOfShards int
    WaitForSync    bool
    AllowUserKeys  bool
    Volatile       bool
    Compactable    bool
}

func (this Collection) migrate(action Action, db *arangolite.Database) error {
    return nil
}

在不久的将来,我需要为索引添加结构.图和其他与ArangoDB相关的功能.每个Yaml文件都需要进入一个生成和迁移的结构,这只是具有该结构当前显示的一个功能的接口的一个实例.

In the near future I need to add structs for Indexes. Graphs and other ArangoDB related features. Each Yaml file needs to go into a struct that produces and migration, which is just a instance of an interfaces with the one function currently shown along with the struct.

推荐答案

我在reddit上找到了此问题的答案,以帮助将来的读者,我在下面提供内容,并提供指向该文章的链接.

I found answer to this question on reddit , to help future readers I am providing content below with link to the post where I found it.

我认为,解决方案是将 t 而不是& t 传递给Unmarshal.

The solution, I believe, is to simply pass t, not &t to Unmarshal.

合理性:您需要将指针传递给可以采用在Unmarshal的界面{}中将预期数据作为动态值.从pickT,您将返回new(Collection),它就是这样的一个指针.但是之后,您首先将其存储在另一个接口类型(迁移)中,然后采用的指针.意思是,yaml将a)看到它已通过a指针和b)尝试将值存储在pointee中.但是那个尖尖的人是接口类型(然后该接口值将包含实际的结构,但yaml不会执行此其他重定向).

Rationale: You need to pass a pointer to a struct which can take the intended data as a dynamic value in the interface{} to Unmarshal. From pickT, you return new(Collection), which is such a pointer. But then, you first store it in another interface type (Migration) and then take the pointer of that. Meaning, yaml will a) see that it gets passed a pointer and b) try to store the value in the pointee. But the pointee is of interface type (and that interface value will then contain the actual struct, but yaml doesn't do this additional redirection).

如果您改为传递t(具有接口类型),它将首先是转换为interface {},然后继续传递-因此参数将直接包含* Collection.

If you instead pass t (which has interface type) it will first be converted to interface{} and then passed on - so the argument will directly contain a *Collection, as intended.

这里的区别是,如果您转换/分配一种接口类型到另一个,它不会被重新包装,而是其中的动态值值将被转移到另一个.但是,如果您存储一个非接口类型(如指向接口的指针),它将得到重新包装.

The difference here is, that if you convert/assign one interface type to another, it won't get re-wrapped, but the dynamic value in one value will just be transferred to another. But if you store a non-interface type (like a pointer to an interface), it will get re-wrapped.

恐怕很难解释.你可以在这里看到效果也是: https://play.golang.org/p/ia181c_Pwp (请注意,该Printf还需要一个接口{})

It's not easily explained, I'm afraid. You can see the effect here too: https://play.golang.org/p/ia181c_Pwp (note, that Printf also takes an interface{})

https://www.reddit.com/r/golang/评论/7k0kpv/please_help_with_dynamically_unmarshaling_yaml_in/

这篇关于在Golang中动态解组Yaml的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆