Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parsing array of string or single string #100

Open
ldesplat opened this issue May 2, 2015 · 12 comments
Open

Parsing array of string or single string #100

ldesplat opened this issue May 2, 2015 · 12 comments

Comments

@ldesplat
Copy link

ldesplat commented May 2, 2015

I need to parse some files and some fields can either include an array of strings or can just include 1 element that's inlined. How do I parse such a file?

field: value

or

field:
  - value1
  - value2

I tried field []string but that of course only works for the bottom example and field string only works for the top one. I thought maybe field []string ",inline" but that's limited to structs. Any idea on how to achieve it? One, could think of the inlined string as an array of 1 length...

@dmacvicar
Copy link

I have the same problem. Did you find a solution meanwhile?

@dmacvicar
Copy link

I managed to get something working using the Unmarshall interface

package main

import (
    "fmt"
    "gopkg.in/yaml.v2"
)

const (
    data = `
attrs:
  foo: bar
  bar:
   - 1
   - 2
   - 3
`
)

type SingleOrMulti struct {
    Values []string
}

func (sm *SingleOrMulti) UnmarshalYAML(unmarshal func(interface{}) error) error {
    var multi []string
    err := unmarshal(&multi)
    if err != nil {
        var single string
        err := unmarshal(&single)
        if err != nil {
            return err
        }
        sm.Values = make([]string, 1)
        sm.Values[0] = single
    } else {
        sm.Values = multi
    }
    return nil
}

type Data struct {
    Attrs map[string]SingleOrMulti
}

func main() {
    var t Data
    yaml.Unmarshal([]byte(data), &t)
    fmt.Printf("%d\n", len(t.Attrs))
    for k, e := range t.Attrs {
        fmt.Printf("%v: %v\n", k, e.Values)
    }
}

Using this I always have a slice in the unmarshaled struct, which gets a single element in case the YAML was a single value.

Note that in this example, attrs: had arbitrary key names so I used a map. You can as well use a struct, if the names are fixed:

type Data struct {
    Foo SingleOrMulti
    Bar SingleOrMulti
}

@ldesplat
Copy link
Author

@dmacvicar I had left this as a todo for later on so thank you very much for finding a solution. You have just taught me how to use this library even more effectively. I did not realize there was an Unmarshaler type! I can now parse some other more complex values right from the beginning. Very awesome.

ldesplat added a commit to ldesplat/docker-gompose that referenced this issue Jun 10, 2015
@dmacvicar
Copy link

You closed the issue, but I still think this is an issue. It would be great if one could do something like:

type Data struct {
   Field []string `yaml:"alwaysarray"`
}

And no matter if someone writes:

field: foobar

That would put the value as the only element of the array.

@dolfelt
Copy link

dolfelt commented Aug 25, 2017

@dmacvicar A slightly modified version of the code above gives you something similar to what you're asking for.

type StringArray []string

func (a *StringArray) UnmarshalYAML(unmarshal func(interface{}) error) error {
	var multi []string
	err := unmarshal(&multi)
	if err != nil {
		var single string
		err := unmarshal(&single)
		if err != nil {
			return err
		}
		*a = []string{single}
	} else {
		*a = multi
	}
	return nil
}

type Data struct {
   Field StringArray
}

Now you can access Data.Field as a string array no matter what.

@StarpTech
Copy link

What's the status? It would be awesome to land this feature.

@iredmail
Copy link

iredmail commented May 2, 2021

Any update on this? It should be very good to have this feature.

@eaglemoor
Copy link

+1

@ristomcgehee
Copy link

Building off of dolfelt's code, here's how you could workaround this if you're using v3 of this package:

type StringArray []string

func (a *StringArray) UnmarshalYAML(value *yaml.Node) error {
	var multi []string
	err := value.Decode(&multi)
	if err != nil {
		var single string
		err := value.Decode(&single)
		if err != nil {
			return err
		}
		*a = []string{single}
	} else {
		*a = multi
	}
	return nil
}

type Data struct {
   Field StringArray
}

@polkeli
Copy link

polkeli commented Jul 12, 2023

I ran into this issue recently and have added a patch in a fork here. I'll work to get a full patch submitted as a PR on this repo.

@polkeli
Copy link

polkeli commented Jul 13, 2023

PR added #974. Please let me know if there's anything missing and/or necessary. Happy to update where necessary.

@metux
Copy link

metux commented Oct 19, 2023

Can we move that ticket to some FAQ ?
Right now it looks like an open issue in the ticket list, but actually it's an answered common question.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants