0

I am writing some code that parses a YAML file and validates the data inside. Each parser returns an error if the value found does not meet the requirements. Right now I am creating an array of errors and setting the return of each call to a different index in the array. My current implementation is working, but it seems wrong and I want to see if there is a better way to accomplish this.

Here is an example

func createStruct(yamlMap map[interface{}]interface{}) (myStruct, error) {
    errs := make([]error, 6)
    s := myStruct{}

    s.Name, errs[0] = getString(yamlMap, "name", true)
    s.Tag, errs[1] = getIntValidRange(yamlMap, "tag", 1, 4094, true)
    s.TaggedPorts, errs[2] = getStringPortList(yamlMap, "tagged_ports", true)
    s.UntaggedPorts, errs[3] = getStringPortList(yamlMap, "untagged_ports", true)
    s.IP, errs[4] = getIPString(yamlMap, "ip", true)
    s.Netmask, errs[5] = getIPString(yamlMap, "netmask", true)

    return s, structCreateErrorChecker(errs)
}

The reason I don't handle each error after each function, is because I want to try to parse everything first and then collect all errors and log them. That is what structCreateErrorChecker() does.

Here is the YAML I am trying to parse (it's come up in the comments). When I say dynamic I mean that there can be any number of these controlling_bridge sections and each vlan section can have any number of vlans.

controlling_bridge_1:
    ip:    "1.1.1.1"
    ports: ["1","2"]
    vlans:
        vlan01:
            name:  "vlan1"
            tag:   1001
            ports: ["1"]
            ip:    "2.2.2.2"
        vlan02:
            name:  "vlan02"
            tag:   1002
            ports: ["3", "4"]
            ip:    "3.3.3.1"

controlling_bridge_2:
    ip:    "1.1.1.1"
    ports: ["1","2"]
    vlans:
        vlan01:
            name:  "vlan1"
            tag:   1001
            ports: ["1"]
            ip:    "2.2.2.2"
        vlan02:
            name:  "vlan02"
            tag:   1002
            ports: ["3", "4"]
            ip:    "3.3.3.1"
8
  • 1
    Why don't you just unmarshal your (apparently) YAML input into the struct you want, rather than using this confusing getString() approach from a map? Commented Oct 5, 2018 at 14:02
  • I originally tried to unmarshall the YAML directly into my structs, but due to the dynamic nature of the YAML file I was having a lot of trouble. So instead I unmarshall it into an interface{} and parse that. Commented Oct 5, 2018 at 14:05
  • 1
    If you can parse it this way, you can parse it using unmarshaling. There's no logical difference. Commented Oct 5, 2018 at 14:06
  • What about your YAML input is "dynamic"? Commented Oct 5, 2018 at 14:07
  • Updated my post with info about my YAML. Commented Oct 5, 2018 at 14:24

1 Answer 1

2

There is no official yaml library, but gopkg.in/yaml.v2 is a good choice. To unmarshal the given yaml you can define structs and add yaml tags to the attributes.

By using maps for your bridges and vlans and using arrays for the ports you can unmarshal the data without a problem.

As you are using maps, keep in mind that iterating over a map does not guarantee the order of returned elements.

This program would unmarshal your given structure:

package main

import (
    "fmt"
    "log"

    yaml "gopkg.in/yaml.v2"
)

var data = `
controlling_bridge_1:
    ip:    "1.1.1.1"
    ports: ["1","2"]
    vlans:
        vlan01:
            name:  "vlan1"
            tag:   1001
            ports: ["1"]
            ip:    "2.2.2.2"
        vlan02:
            name:  "vlan02"
            tag:   1002
            ports: ["3", "4"]
            ip:    "3.3.3.1"

controlling_bridge_2:
    ip:    "1.1.1.1"
    ports: ["1","2"]
    vlans:
        vlan01:
            name:  "vlan1"
            tag:   1001
            ports: ["1"]
            ip:    "2.2.2.2"
        vlan02:
            name:  "vlan02"
            tag:   1002
            ports: ["3", "4"]
            ip:    "3.3.3.1"
`

type Bridge struct {
    IP    string   `yaml:"ip"`
    Ports []string `yaml:"ports"`
    Vlans map[string]Vlan
}

type Vlan struct {
    Name  string   `yaml:"name"`
    Tag   string   `yaml:"tag"`
    Ports []string `yaml:"ports"`
    IP    string   `yaml:"ip"`
}

func main() {
    bridges := map[string]Bridge{}

    err := yaml.Unmarshal([]byte(data), &bridges)
    if err != nil {
        log.Fatalf("error: %v", err)
    }

    fmt.Printf("%+v\n", bridges)
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.