diff --git a/decode.go b/decode.go index b2e16a6d..366431c0 100644 --- a/decode.go +++ b/decode.go @@ -632,12 +632,9 @@ func (d *decoder) scalar(n *Node, out reflect.Value) bool { case string: // This offers some compatibility with the 1.1 spec (https://yaml.org/type/bool.html). // It only works if explicitly attempting to unmarshal into a typed bool value. - switch resolved { - case "y", "Y", "yes", "Yes", "YES", "on", "On", "ON": - out.SetBool(true) - return true - case "n", "N", "no", "No", "NO", "off", "Off", "OFF": - out.SetBool(false) + val, found := boolMap[resolved] + if found { + out.SetBool(val) return true } } diff --git a/encode.go b/encode.go index 38882b50..185a4933 100644 --- a/encode.go +++ b/encode.go @@ -320,6 +320,11 @@ func (e *encoder) stringv(tag string, in reflect.Value) { // there's no need to quote it. rtag, _ := resolve("", s) canUsePlain = rtag == strTag && !isBase60Float(s) + + // Check to see if this is a v1.1 yaml bool string + // if it is, require quotes to preserve backwards compatibility + _, is11BoolString := boolMap[s] + canUsePlain = !is11BoolString && canUsePlain } // Note: it's possible for user code to emit invalid YAML // if they explicitly specify a tag and a string containing diff --git a/encode_test.go b/encode_test.go index 0f01b76f..42d763c7 100644 --- a/encode_test.go +++ b/encode_test.go @@ -422,6 +422,39 @@ var marshalTests = []struct { map[string]string{"a": "\tB\n\tC\n"}, "a: |\n \tB\n \tC\n", }, + // yaml 1.1 bool strings should be marshalled with quotes to preserve compatibility + { + map[string]string{"a": "y", "b": "n"}, + "a: \"y\"\nb: \"n\"\n", + }, + { + map[string]string{"a": "Y", "b": "N"}, + "a: \"Y\"\nb: \"N\"\n", + }, + { + map[string]string{"a": "yes", "b": "no"}, + "a: \"yes\"\nb: \"no\"\n", + }, + { + map[string]string{"a": "Yes", "b": "No"}, + "a: \"Yes\"\nb: \"No\"\n", + }, + { + map[string]string{"a": "YES", "b": "NO"}, + "a: \"YES\"\nb: \"NO\"\n", + }, + { + map[string]string{"a": "on", "b": "off"}, + "a: \"on\"\nb: \"off\"\n", + }, + { + map[string]string{"a": "On", "b": "Off"}, + "a: \"On\"\nb: \"Off\"\n", + }, + { + map[string]string{"a": "ON", "b": "OFF"}, + "a: \"ON\"\nb: \"OFF\"\n", + }, } func (s *S) TestMarshal(c *C) { diff --git a/resolve.go b/resolve.go index 64ae8880..acfb55d8 100644 --- a/resolve.go +++ b/resolve.go @@ -31,6 +31,7 @@ type resolveMapItem struct { var resolveTable = make([]byte, 256) var resolveMap = make(map[string]resolveMapItem) +var boolMap = make(map[string]bool) func init() { t := resolveTable @@ -65,6 +66,26 @@ func init() { m[s] = resolveMapItem{item.v, item.tag} } } + + // the remaining bool values from the 1.1 spec (https://yaml.org/type/bool.html) + boolMap = map[string]bool{ + "y": true, + "Y": true, + "yes": true, + "Yes": true, + "YES": true, + "on": true, + "On": true, + "ON": true, + "n": false, + "N": false, + "no": false, + "No": false, + "NO": false, + "off": false, + "Off": false, + "OFF": false, + } } const (