golang

时间:2017-12-15 06:36:53

标签: go yaml

编辑:添加问题上下文

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

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

所有这一切,我有几种不同类型的结构存储在不同的yaml文件中。我可以通过读取yaml的第一行来判断哪个结构位于哪个文件中。我想通过阅读文件尽可能干燥。

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

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解决了以下错误。

--- 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将其转换出来?我知道我可以通过在不同的功能中反复重复相同的逻辑来手动执行此操作,如下图所示,但这看起来很糟糕。

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

编辑:添加Collection结构和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文件都需要进入一个生成和迁移的结构,这只是一个接口的实例,其中一个函数当前与结构一起显示。

1 个答案:

答案 0 :(得分:0)

我在reddit上找到了该问题的答案,以帮助未来的读者,我在下面提供的内容带有我找到它的帖子的链接。

我认为,解决方案是简单地将t而不是&t传递给Unmarshal。

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

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

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

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

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