Fixed tag support for r59. Tweaked flag style.
diff --git a/encode.go b/encode.go
index ba15123..ad414e2 100644
--- a/encode.go
+++ b/encode.go
@@ -143,7 +143,7 @@
e.mappingv(tag, func() {
for i, info := range fields.List {
value := in.Field(i)
- if info.Conditional && isZero(value) {
+ if info.OmitEmpty && isZero(value) {
continue
}
e.marshal("", reflect.ValueOf(info.Key))
diff --git a/encode_test.go b/encode_test.go
index bc49558..3fd69c5 100644
--- a/encode_test.go
+++ b/encode_test.go
@@ -70,27 +70,27 @@
// Conditional flag
{"a: 1\n", &struct {
- A int "a/c"
- B int "b/c"
+ A int "a,omitempty"
+ B int "b,omitempty"
}{1, 0}},
{"{}\n", &struct {
- A int "a/c"
- B int "b/c"
+ A int "a,omitempty"
+ B int "b,omitempty"
}{0, 0}},
// Flow flag
{"a: [1, 2]\n", &struct {
- A []int "a/f"
+ A []int "a,flow"
}{[]int{1, 2}}},
{"a: {b: c}\n",
&struct {
- A map[string]string "a/f"
+ A map[string]string "a,flow"
}{map[string]string{"b": "c"}}},
{"a: {b: c}\n",
&struct {
A struct {
B string
- } "a/f"
+ } "a,flow"
}{struct{ B string }{"c"}}},
}
diff --git a/goyaml.go b/goyaml.go
index 60bd682..7c2d98e 100644
--- a/goyaml.go
+++ b/goyaml.go
@@ -10,6 +10,7 @@
package goyaml
import (
+ "fmt"
"reflect"
"runtime"
"strings"
@@ -23,6 +24,8 @@
panic(r)
} else if _, ok := r.(*reflect.ValueError); ok {
panic(r)
+ } else if _, ok := r.(externalPanic); ok {
+ panic(r)
} else if s, ok := r.(string); ok {
*err = os.NewError("YAML error: " + s)
} else if e, ok := r.(os.Error); ok {
@@ -68,14 +71,14 @@
// Struct fields are only unmarshalled if they are exported (have an
// upper case first letter), and will be unmarshalled using the field
// name lowercased by default. When custom field names are desired, the
-// tag value may be used to tweak the name. Everything before the last
-// slash in the field tag will be used as the name. The value following
-// the slash is used to tweak the marshalling process (see Marshal).
+// tag value may be used to tweak the name. Everything before the first
+// comma in the field tag will be used as the name. The values following
+// the comma are used to tweak the marshalling process (see Marshal).
//
// For example:
//
// type T struct {
-// F int "a/c"
+// F int "a,omitempty"
// B int
// }
// var T t
@@ -93,23 +96,33 @@
return nil
}
-// Marshal writes the object provided into a YAML document. The structure
+// Marshal serializes the value provided into a YAML document. The structure
// of the generated document will reflect the structure of the value itself.
// Maps, pointers to structs and ints, etc, may all be used as the in value.
//
-// Struct fields are only marshalled if they are exported (have an
-// upper case first letter), and will be marshalled using the field
-// name lowercased by default. When custom field names are desired, the
-// tag value may be used to tweak the name. Everything before the last
-// slash in the field tag will be used as the name. The characters
-// following the slash are used as flags for the marshalling process,
-// with 'c' meaning conditional (only marshal if non-zero), and 'f'
-// meaning use flow style (useful for structs, sequences and maps).
+// In the case of struct values, only exported fields will be serialized.
+// The lowercased field name is used as the key for each exported field,
+// but this behavior may be changed using the respective field tag.
+// The tag may also contain flags to tweak the marshalling behavior for
+// the field. The tag formats accepted are:
+//
+// "[<key>][,<flag1>[,<flag2>]]"
+//
+// `(...) yaml:"[<key>][,<flag1>[,<flag2>]]" (...)`
+//
+// The following flags are currently supported:
+//
+// omitempty Only include the field if it's not set to the zero
+// value for the type or to empty slices or maps.
+// Does not apply to zero valued structs.
+//
+// flow Marshal using a flow style (useful for structs,
+// sequences and maps.
//
// For example:
//
// type T struct {
-// F int "a/c"
+// F int "a,omitempty"
// B int
// }
// goyaml.Marshal(&T{B: 2}) // Returns "b: 2\n"
@@ -137,15 +150,21 @@
}
type fieldInfo struct {
- Key string
- Num int
- Conditional bool
- Flow bool
+ Key string
+ Num int
+ OmitEmpty bool
+ Flow bool
}
var fieldMap = make(map[string]*structFields)
var fieldMapMutex sync.RWMutex
+type externalPanic string
+
+func (e externalPanic) String() string {
+ return string(e)
+}
+
func getStructFields(st reflect.Type) (*structFields, os.Error) {
path := st.PkgPath()
name := st.Name()
@@ -169,22 +188,47 @@
info := fieldInfo{Num: i}
- if s := strings.LastIndex(field.Tag, "/"); s != -1 {
- for _, c := range field.Tag[s+1:] {
- switch c {
- case int('c'):
- info.Conditional = true
- case int('f'):
- info.Flow = true
- default:
- panic("Unsupported field flag: " + string([]int{c}))
- }
- }
- field.Tag = field.Tag[:s]
+ tag := field.Tag.Get("yaml")
+ if tag == "" && strings.Index(string(field.Tag), ":") < 0 {
+ tag = string(field.Tag)
}
- if field.Tag != "" {
- info.Key = field.Tag
+ // XXX Drop this after a few releases.
+ if s := strings.Index(tag, "/"); s >= 0 {
+ recommend := tag[:s]
+ for _, c := range tag[s+1:] {
+ switch c {
+ case int('c'):
+ recommend += ",omitempty"
+ case int('f'):
+ recommend += ",flow"
+ default:
+ msg := fmt.Sprintf("Unsupported flag %q in tag %q of type %s", string([]byte{uint8(c)}), tag, st)
+ panic(externalPanic(msg))
+ }
+ }
+ msg := fmt.Sprintf("Replace tag %q in field %s of type %s by %q", tag, field.Name, st, recommend)
+ panic(externalPanic(msg))
+ }
+
+ fields := strings.Split(tag, ",")
+ if len(fields) > 1 {
+ for _, flag := range fields[1:] {
+ switch flag {
+ case "omitempty":
+ info.OmitEmpty = true
+ case "flow":
+ info.Flow = true
+ default:
+ msg := fmt.Sprintf("Unsupported flag %q in tag %q of type %s", flag, tag, st)
+ panic(externalPanic(msg))
+ }
+ }
+ tag = fields[0]
+ }
+
+ if tag != "" {
+ info.Key = tag
} else {
info.Key = strings.ToLower(field.Name)
}