From 1d653a737648051ca638423377052c2f5c10c050 Mon Sep 17 00:00:00 2001 From: Robert Gogolok Date: Wed, 4 Feb 2015 13:11:21 +0100 Subject: [PATCH 01/40] travis ci --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..98b43464 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,5 @@ +language: go + +go: + - 1.3 + - tip From a347d2466e459933f4fb25f8026d995977436ccf Mon Sep 17 00:00:00 2001 From: Robert Gogolok Date: Wed, 4 Feb 2015 13:15:35 +0100 Subject: [PATCH 02/40] README.md: travis ci build status [ci skip] --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index d6c919e6..70f92bdd 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # YAML support for the Go language +[![Build Status](https://travis-ci.org/go-yaml/yaml.svg?branch=travis_ci_support)](https://travis-ci.org/go-yaml/yaml) + Introduction ------------ From df747160af0ebfcc572951e4168d4b1bc91a47f5 Mon Sep 17 00:00:00 2001 From: Robert Date: Wed, 11 Feb 2015 18:27:59 +0100 Subject: [PATCH 03/40] Balance parentheses. --- yaml.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yaml.go b/yaml.go index 6af88c00..af4df8a4 100644 --- a/yaml.go +++ b/yaml.go @@ -117,7 +117,7 @@ func Unmarshal(in []byte, out interface{}) (err error) { // Does not apply to zero valued structs. // // flow Marshal using a flow style (useful for structs, -// sequences and maps. +// sequences and maps). // // inline Inline the field, which must be a struct or a map, // causing all of its fields or keys to be processed as if From 50f7813e6b19e58334360ab011dfbaece5b1501f Mon Sep 17 00:00:00 2001 From: Gustavo Niemeyer Date: Tue, 24 Feb 2015 15:24:13 -0300 Subject: [PATCH 04/40] Add time.Time encoding/decoding test. It's not clear this is actually the best way to handle time.Time, since there is a standard yaml format that may not match time.Time's, but having tests ensuring the current behavior is basically sane is a good idea nevertheless. --- decode_test.go | 4 ++++ encode_test.go | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/decode_test.go b/decode_test.go index 179f2cea..593406b7 100644 --- a/decode_test.go +++ b/decode_test.go @@ -549,6 +549,10 @@ var unmarshalTests = []struct { "a: 1.2.3.4\n", map[string]net.IP{"a": net.IPv4(1, 2, 3, 4)}, }, + { + "a: 2015-02-24T15:19:39-03:00\n", + map[string]time.Time{"a": time.Unix(1424801979, 0)}, + }, // Encode empty lists as zero-length slices. { diff --git a/encode_test.go b/encode_test.go index e340f58c..a948e178 100644 --- a/encode_test.go +++ b/encode_test.go @@ -295,6 +295,10 @@ var marshalTests = []struct { map[string]net.IP{"a": net.IPv4(1, 2, 3, 4)}, "a: 1.2.3.4\n", }, + { + map[string]time.Time{"a": time.Unix(1424801979, 0)}, + "a: 2015-02-24T15:19:39-03:00\n", + }, // Ensure strings containing ": " are quoted (reported as PR #43, but not reproducible). { From d0fefed9b627fbe0c1597ac29ed5f48ff2eb9064 Mon Sep 17 00:00:00 2001 From: Gustavo Niemeyer Date: Tue, 24 Feb 2015 19:53:31 -0300 Subject: [PATCH 05/40] Fix timezone handling in time tests. --- decode_test.go | 2 +- encode_test.go | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/decode_test.go b/decode_test.go index 593406b7..04fdd9e7 100644 --- a/decode_test.go +++ b/decode_test.go @@ -550,7 +550,7 @@ var unmarshalTests = []struct { map[string]net.IP{"a": net.IPv4(1, 2, 3, 4)}, }, { - "a: 2015-02-24T15:19:39-03:00\n", + "a: 2015-02-24T18:19:39Z\n", map[string]time.Time{"a": time.Unix(1424801979, 0)}, }, diff --git a/encode_test.go b/encode_test.go index a948e178..ba68ad29 100644 --- a/encode_test.go +++ b/encode_test.go @@ -10,6 +10,7 @@ import ( . "gopkg.in/check.v1" "gopkg.in/yaml.v2" "net" + "os" ) var marshalIntTest = 123 @@ -297,7 +298,7 @@ var marshalTests = []struct { }, { map[string]time.Time{"a": time.Unix(1424801979, 0)}, - "a: 2015-02-24T15:19:39-03:00\n", + "a: 2015-02-24T18:19:39Z\n", }, // Ensure strings containing ": " are quoted (reported as PR #43, but not reproducible). @@ -308,6 +309,8 @@ var marshalTests = []struct { } func (s *S) TestMarshal(c *C) { + defer os.Setenv("TZ", os.Getenv("TZ")) + os.Setenv("TZ", "UTC") for _, item := range marshalTests { data, err := yaml.Marshal(item.value) c.Assert(err, IsNil) From 49c95bdc21843256fb6c4e0d370a05f24a0bf213 Mon Sep 17 00:00:00 2001 From: Gustavo Niemeyer Date: Tue, 24 Feb 2015 19:57:58 -0300 Subject: [PATCH 06/40] Drop travis support. Not working and not missed. --- .travis.yml | 5 ----- README.md | 2 -- 2 files changed, 7 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 98b43464..00000000 --- a/.travis.yml +++ /dev/null @@ -1,5 +0,0 @@ -language: go - -go: - - 1.3 - - tip diff --git a/README.md b/README.md index 70f92bdd..d6c919e6 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # YAML support for the Go language -[![Build Status](https://travis-ci.org/go-yaml/yaml.svg?branch=travis_ci_support)](https://travis-ci.org/go-yaml/yaml) - Introduction ------------ From 43a0256bb22b0c2e1803ac6e28f55e5989a60523 Mon Sep 17 00:00:00 2001 From: Songmu Date: Tue, 19 May 2015 19:38:58 +0900 Subject: [PATCH 07/40] add failing test cases --- encode_test.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/encode_test.go b/encode_test.go index ba68ad29..1b35452c 100644 --- a/encode_test.go +++ b/encode_test.go @@ -306,6 +306,16 @@ var marshalTests = []struct { map[string]string{"a": "b: c"}, "a: 'b: c'\n", }, + + // Containing hash mark ('#') in string should be quoted + { + map[string]string{"a": "Hello #comment"}, + "a: 'Hello #comment'\n", + }, + { + map[string]string{"a": "你好 #comment"}, + "a: '你好 #comment'\n", + }, } func (s *S) TestMarshal(c *C) { From c1cd2254a6dd314c9d73c338c12688c9325d85c6 Mon Sep 17 00:00:00 2001 From: Songmu Date: Tue, 19 May 2015 19:42:33 +0900 Subject: [PATCH 08/40] fix bug: width() always returns width of first character! --- emitterc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/emitterc.go b/emitterc.go index 9b3dc4a4..2befd553 100644 --- a/emitterc.go +++ b/emitterc.go @@ -1019,7 +1019,7 @@ func yaml_emitter_analyze_scalar(emitter *yaml_emitter_t, value []byte) bool { preceeded_by_whitespace = true for i, w := 0, 0; i < len(value); i += w { - w = width(value[0]) + w = width(value[i]) followed_by_whitespace = i+w >= len(value) || is_blank(value, i+w) if i == 0 { From 9eade332f0ceebc6b7c9e24893574cad4c51722b Mon Sep 17 00:00:00 2001 From: Andrey Chernih Date: Mon, 25 May 2015 14:24:17 -0700 Subject: [PATCH 09/40] Fix for non-specific tags parsing --- decode_test.go | 6 ++++++ scannerc.go | 8 +++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/decode_test.go b/decode_test.go index 04fdd9e7..878582ea 100644 --- a/decode_test.go +++ b/decode_test.go @@ -405,6 +405,12 @@ var unmarshalTests = []struct { map[string]interface{}{"v": 1}, }, + // Non-specific tag (Issue #75) + { + "v: ! test", + map[string]interface{}{"v": "test"}, + }, + // Anchors and aliases. { "a: &x 1\nb: &y 2\nc: *x\nd: *y\n", diff --git a/scannerc.go b/scannerc.go index fe93b190..ed212db3 100644 --- a/scannerc.go +++ b/scannerc.go @@ -611,7 +611,7 @@ func yaml_parser_set_scanner_tag_error(parser *yaml_parser_t, directive bool, co if directive { context = "while parsing a %TAG directive" } - return yaml_parser_set_scanner_error(parser, context, context_mark, "did not find URI escaped octet") + return yaml_parser_set_scanner_error(parser, context, context_mark, problem) } func trace(args ...interface{}) func() { @@ -1959,11 +1959,12 @@ func yaml_parser_scan_tag_handle(parser *yaml_parser_t, directive bool, start_ma func yaml_parser_scan_tag_uri(parser *yaml_parser_t, directive bool, head []byte, start_mark yaml_mark_t, uri *[]byte) bool { //size_t length = head ? strlen((char *)head) : 0 var s []byte + length := len(head) // Copy the head if needed. // // Note that we don't copy the leading '!' character. - if len(head) > 1 { + if length > 0 { s = append(s, head[1:]...) } @@ -1996,6 +1997,7 @@ func yaml_parser_scan_tag_uri(parser *yaml_parser_t, directive bool, head []byte } } else { s = read(parser, s) + length++ } if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false @@ -2003,7 +2005,7 @@ func yaml_parser_scan_tag_uri(parser *yaml_parser_t, directive bool, head []byte } // Check if the tag is non-empty. - if len(s) == 0 { + if length == 0 { yaml_parser_set_scanner_tag_error(parser, directive, start_mark, "did not find expected tag URI") return false From b9b22c434500d7639936fbed673fc0ef23ce88f6 Mon Sep 17 00:00:00 2001 From: Robert Vollmert Date: Mon, 1 Jun 2015 16:52:26 +0200 Subject: [PATCH 10/40] gofmt --- encode_test.go | 2 +- yaml.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/encode_test.go b/encode_test.go index 1b35452c..98aa7f21 100644 --- a/encode_test.go +++ b/encode_test.go @@ -340,7 +340,7 @@ var marshalErrorTests = []struct { panic: `Duplicated key 'b' in struct struct \{ B int; .*`, }, { value: &struct { - A int + A int B map[string]int ",inline" }{1, map[string]int{"a": 2}}, panic: `Can't have key "a" in inlined map; conflicts with struct field`, diff --git a/yaml.go b/yaml.go index af4df8a4..6f55f20a 100644 --- a/yaml.go +++ b/yaml.go @@ -330,7 +330,7 @@ func isZero(v reflect.Value) bool { return !v.Bool() case reflect.Struct: vt := v.Type() - for i := v.NumField()-1; i >= 0; i-- { + for i := v.NumField() - 1; i >= 0; i-- { if vt.Field(i).PkgPath != "" { continue // Private field } From 7ad95dd0798a40da1ccdff6dff35fd177b5edf40 Mon Sep 17 00:00:00 2001 From: Gustavo Niemeyer Date: Wed, 24 Jun 2015 11:29:02 +0100 Subject: [PATCH 11/40] Fix omitempty support for floats. --- encode_test.go | 8 +++++++- yaml.go | 4 +++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/encode_test.go b/encode_test.go index 1b35452c..84099bd3 100644 --- a/encode_test.go +++ b/encode_test.go @@ -190,6 +190,12 @@ var marshalTests = []struct { A struct{ X, y int } "a,omitempty,flow" }{struct{ X, y int }{0, 1}}, "{}\n", + }, { + &struct { + A float64 "a,omitempty" + B float64 "b,omitempty" + }{1, 0}, + "a: 1\n", }, // Flow flag @@ -340,7 +346,7 @@ var marshalErrorTests = []struct { panic: `Duplicated key 'b' in struct struct \{ B int; .*`, }, { value: &struct { - A int + A int B map[string]int ",inline" }{1, map[string]int{"a": 2}}, panic: `Can't have key "a" in inlined map; conflicts with struct field`, diff --git a/yaml.go b/yaml.go index af4df8a4..d133edf9 100644 --- a/yaml.go +++ b/yaml.go @@ -324,13 +324,15 @@ func isZero(v reflect.Value) bool { return v.Len() == 0 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return v.Int() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return v.Uint() == 0 case reflect.Bool: return !v.Bool() case reflect.Struct: vt := v.Type() - for i := v.NumField()-1; i >= 0; i-- { + for i := v.NumField() - 1; i >= 0; i-- { if vt.Field(i).PkgPath != "" { continue // Private field } From 0516c53462e633a479f3826e1d3557033413eeb8 Mon Sep 17 00:00:00 2001 From: Sameer Vohra Date: Tue, 18 Aug 2015 14:29:51 -0400 Subject: [PATCH 12/40] fixed typo in scannerc.go intendation->indentation --- scannerc.go | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/scannerc.go b/scannerc.go index fe93b190..d97d76fa 100644 --- a/scannerc.go +++ b/scannerc.go @@ -961,7 +961,7 @@ func yaml_parser_roll_indent(parser *yaml_parser_t, column, number int, typ yaml } // Pop indentation levels from the indents stack until the current level -// becomes less or equal to the column. For each intendation level, append +// becomes less or equal to the column. For each indentation level, append // the BLOCK-END token. func yaml_parser_unroll_indent(parser *yaml_parser_t, column int) bool { // In the flow context, do nothing. @@ -969,7 +969,7 @@ func yaml_parser_unroll_indent(parser *yaml_parser_t, column int) bool { return true } - // Loop through the intendation levels in the stack. + // Loop through the indentation levels in the stack. for parser.indent > column { // Create a token and append it to the queue. token := yaml_token_t{ @@ -2085,14 +2085,14 @@ func yaml_parser_scan_block_scalar(parser *yaml_parser_t, token *yaml_token_t, l return false } if is_digit(parser.buffer, parser.buffer_pos) { - // Check that the intendation is greater than 0. + // Check that the indentation is greater than 0. if parser.buffer[parser.buffer_pos] == '0' { yaml_parser_set_scanner_error(parser, "while scanning a block scalar", - start_mark, "found an intendation indicator equal to 0") + start_mark, "found an indentation indicator equal to 0") return false } - // Get the intendation level and eat the indicator. + // Get the indentation level and eat the indicator. increment = as_digit(parser.buffer, parser.buffer_pos) skip(parser) } @@ -2102,7 +2102,7 @@ func yaml_parser_scan_block_scalar(parser *yaml_parser_t, token *yaml_token_t, l if parser.buffer[parser.buffer_pos] == '0' { yaml_parser_set_scanner_error(parser, "while scanning a block scalar", - start_mark, "found an intendation indicator equal to 0") + start_mark, "found an indentation indicator equal to 0") return false } increment = as_digit(parser.buffer, parser.buffer_pos) @@ -2157,7 +2157,7 @@ func yaml_parser_scan_block_scalar(parser *yaml_parser_t, token *yaml_token_t, l end_mark := parser.mark - // Set the intendation level if it was specified. + // Set the indentation level if it was specified. var indent int if increment > 0 { if parser.indent >= 0 { @@ -2217,7 +2217,7 @@ func yaml_parser_scan_block_scalar(parser *yaml_parser_t, token *yaml_token_t, l leading_break = read_line(parser, leading_break) - // Eat the following intendation spaces and line breaks. + // Eat the following indentation spaces and line breaks. if !yaml_parser_scan_block_scalar_breaks(parser, &indent, &trailing_breaks, start_mark, &end_mark) { return false } @@ -2245,15 +2245,15 @@ func yaml_parser_scan_block_scalar(parser *yaml_parser_t, token *yaml_token_t, l return true } -// Scan intendation spaces and line breaks for a block scalar. Determine the -// intendation level if needed. +// Scan indentation spaces and line breaks for a block scalar. Determine the +// indentation level if needed. func yaml_parser_scan_block_scalar_breaks(parser *yaml_parser_t, indent *int, breaks *[]byte, start_mark yaml_mark_t, end_mark *yaml_mark_t) bool { *end_mark = parser.mark - // Eat the intendation spaces and line breaks. + // Eat the indentation spaces and line breaks. max_indent := 0 for { - // Eat the intendation spaces. + // Eat the indentation spaces. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } @@ -2267,10 +2267,10 @@ func yaml_parser_scan_block_scalar_breaks(parser *yaml_parser_t, indent *int, br max_indent = parser.mark.column } - // Check for a tab character messing the intendation. + // Check for a tab character messing the indentation. if (*indent == 0 || parser.mark.column < *indent) && is_tab(parser.buffer, parser.buffer_pos) { return yaml_parser_set_scanner_error(parser, "while scanning a block scalar", - start_mark, "found a tab character where an intendation space is expected") + start_mark, "found a tab character where an indentation space is expected") } // Have we found a non-empty line? @@ -2655,10 +2655,10 @@ func yaml_parser_scan_plain_scalar(parser *yaml_parser_t, token *yaml_token_t) b for is_blank(parser.buffer, parser.buffer_pos) || is_break(parser.buffer, parser.buffer_pos) { if is_blank(parser.buffer, parser.buffer_pos) { - // Check for tab character that abuse intendation. + // Check for tab character that abuse indentation. if leading_blanks && parser.mark.column < indent && is_tab(parser.buffer, parser.buffer_pos) { yaml_parser_set_scanner_error(parser, "while scanning a plain scalar", - start_mark, "found a tab character that violate intendation") + start_mark, "found a tab character that violate indentation") return false } @@ -2687,7 +2687,7 @@ func yaml_parser_scan_plain_scalar(parser *yaml_parser_t, token *yaml_token_t) b } } - // Check intendation level. + // Check indentation level. if parser.flow_level == 0 && parser.mark.column < indent { break } From 2bf60357b89cbc6044dde700cf63bab94a615bf7 Mon Sep 17 00:00:00 2001 From: Gustavo Niemeyer Date: Thu, 24 Sep 2015 10:16:22 -0300 Subject: [PATCH 13/40] Add simple example of field renaming. Fixes #122. --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d6c919e6..7b8bd867 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,10 @@ b: type T struct { A string - B struct{C int; D []int ",flow"} + B struct { + RenamedC int `yaml:"c"` + D []int `yaml:",flow"` + } } func main() { From e90bcf783f7abddaa0ee0994a09e536498744e49 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Tue, 7 Apr 2015 11:02:50 +0200 Subject: [PATCH 14/40] add examples for decoding embedded structs --- README.md | 2 ++ examples/embedded-structs.go | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 examples/embedded-structs.go diff --git a/README.md b/README.md index d6c919e6..89ec9f33 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,8 @@ The yaml package is licensed under the LGPL with an exception that allows it to Example ------- +Some more examples can be found in the "examples" folder. + ```Go package main diff --git a/examples/embedded-structs.go b/examples/embedded-structs.go new file mode 100644 index 00000000..307ab8fa --- /dev/null +++ b/examples/embedded-structs.go @@ -0,0 +1,35 @@ +package main + +import ( + "fmt" + "log" + + "gopkg.in/yaml.v2" +) + +type StructA struct { + A string `yaml:"a"` +} + +type StructB struct { + // go-yaml will not decode embedded structs by default, to do that + // you need to add the ",inline" annotation below + StructA `yaml:",inline"` + B string `yaml:"b"` +} + +var data = ` +a: a string from struct A +b: a string from struct B +` + +func main() { + var b StructB + + err := yaml.Unmarshal([]byte(data), &b) + if err != nil { + log.Fatalf("error: %v", err) + } + fmt.Println(b.A) + fmt.Println(b.B) +} From bd61a856f807e525beaee41959452c88c83d46cf Mon Sep 17 00:00:00 2001 From: Gustavo Niemeyer Date: Tue, 1 Dec 2015 13:41:20 -0200 Subject: [PATCH 15/40] Fix UTF-16 LE and BE handling. Thanks to John Lenton for the test cases. --- decode_test.go | 12 ++++++++++++ readerc.go | 7 +++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/decode_test.go b/decode_test.go index 04fdd9e7..c20855d8 100644 --- a/decode_test.go +++ b/decode_test.go @@ -559,6 +559,18 @@ var unmarshalTests = []struct { "a: []", &struct{ A []int }{[]int{}}, }, + + // UTF-16-LE + { + "\xff\xfe\xf1\x00o\x00\xf1\x00o\x00:\x00 \x00v\x00e\x00r\x00y\x00 \x00y\x00e\x00s\x00\n\x00", + M{"ñoño":"very yes"}, + }, + + // UTF-16-BE + { + "\xfe\xff\x00\xf1\x00o\x00\xf1\x00o\x00:\x00 \x00v\x00e\x00r\x00y\x00 \x00y\x00e\x00s\x00\n", + M{"ñoño":"very yes"}, + }, } type M map[interface{}]interface{} diff --git a/readerc.go b/readerc.go index d5fb0972..f4507917 100644 --- a/readerc.go +++ b/readerc.go @@ -247,7 +247,7 @@ func yaml_parser_update_buffer(parser *yaml_parser_t, length int) bool { if parser.encoding == yaml_UTF16LE_ENCODING { low, high = 0, 1 } else { - high, low = 1, 0 + low, high = 1, 0 } // The UTF-16 encoding is not as simple as one might @@ -357,23 +357,26 @@ func yaml_parser_update_buffer(parser *yaml_parser_t, length int) bool { if value <= 0x7F { // 0000 0000-0000 007F . 0xxxxxxx parser.buffer[buffer_len+0] = byte(value) + buffer_len += 1 } else if value <= 0x7FF { // 0000 0080-0000 07FF . 110xxxxx 10xxxxxx parser.buffer[buffer_len+0] = byte(0xC0 + (value >> 6)) parser.buffer[buffer_len+1] = byte(0x80 + (value & 0x3F)) + buffer_len += 2 } else if value <= 0xFFFF { // 0000 0800-0000 FFFF . 1110xxxx 10xxxxxx 10xxxxxx parser.buffer[buffer_len+0] = byte(0xE0 + (value >> 12)) parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 6) & 0x3F)) parser.buffer[buffer_len+2] = byte(0x80 + (value & 0x3F)) + buffer_len += 3 } else { // 0001 0000-0010 FFFF . 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx parser.buffer[buffer_len+0] = byte(0xF0 + (value >> 18)) parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 12) & 0x3F)) parser.buffer[buffer_len+2] = byte(0x80 + ((value >> 6) & 0x3F)) parser.buffer[buffer_len+3] = byte(0x80 + (value & 0x3F)) + buffer_len += 4 } - buffer_len += width parser.unread++ } From f7716cbe52baa25d2e9b0d0da546fcf909fc16b4 Mon Sep 17 00:00:00 2001 From: Gustavo Niemeyer Date: Tue, 1 Dec 2015 14:27:45 -0200 Subject: [PATCH 16/40] More UTF-16 test cases by John. --- decode_test.go | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/decode_test.go b/decode_test.go index c20855d8..c159760b 100644 --- a/decode_test.go +++ b/decode_test.go @@ -563,13 +563,23 @@ var unmarshalTests = []struct { // UTF-16-LE { "\xff\xfe\xf1\x00o\x00\xf1\x00o\x00:\x00 \x00v\x00e\x00r\x00y\x00 \x00y\x00e\x00s\x00\n\x00", - M{"ñoño":"very yes"}, + M{"ñoño": "very yes"}, + }, + // UTF-16-LE with surrogate. + { + "\xff\xfe\xf1\x00o\x00\xf1\x00o\x00:\x00 \x00v\x00e\x00r\x00y\x00 \x00y\x00e\x00s\x00 \x00=\xd8\xd4\xdf\n\x00", + M{"ñoño": "very yes 🟔"}, }, // UTF-16-BE { "\xfe\xff\x00\xf1\x00o\x00\xf1\x00o\x00:\x00 \x00v\x00e\x00r\x00y\x00 \x00y\x00e\x00s\x00\n", - M{"ñoño":"very yes"}, + M{"ñoño": "very yes"}, + }, + // UTF-16-BE with surrogate. + { + "\xfe\xff\x00\xf1\x00o\x00\xf1\x00o\x00:\x00 \x00v\x00e\x00r\x00y\x00 \x00y\x00e\x00s\x00 \xd8=\xdf\xd4\x00\n", + M{"ñoño": "very yes 🟔"}, }, } From a833012353d046b1f12c82db87d01c86570b24d7 Mon Sep 17 00:00:00 2001 From: Anthony Fok Date: Sun, 20 Dec 2015 07:16:50 -0700 Subject: [PATCH 17/40] =?UTF-8?q?Fix=20typo:=20"uknown"=20=E2=86=92=20?= =?UTF-8?q?=E2=80=9Cunknown"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scannerc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scannerc.go b/scannerc.go index d97d76fa..25808000 100644 --- a/scannerc.go +++ b/scannerc.go @@ -1546,7 +1546,7 @@ func yaml_parser_scan_directive(parser *yaml_parser_t, token *yaml_token_t) bool // Unknown directive. } else { yaml_parser_set_scanner_error(parser, "while scanning a directive", - start_mark, "found uknown directive name") + start_mark, "found unknown directive name") return false } From 406cad6bb47dd7d9a123d005fb8ff766f6463051 Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Wed, 23 Dec 2015 12:11:14 +1300 Subject: [PATCH 18/40] make compatible with Go 1.6 As documented at http://tip.golang.org/doc/go1.6#reflect, the rules around embedded unexported struct types containing exported fields are changing in Go 1.6. The same release notes explain how to change existing code, so do that. --- yaml.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yaml.go b/yaml.go index d133edf9..36d6b883 100644 --- a/yaml.go +++ b/yaml.go @@ -222,7 +222,7 @@ func getStructInfo(st reflect.Type) (*structInfo, error) { inlineMap := -1 for i := 0; i != n; i++ { field := st.Field(i) - if field.PkgPath != "" { + if field.PkgPath != "" && !field.Anonymous { continue // Private field } From d6c23fbaf16f72995b58492627e65801cfb9a8dd Mon Sep 17 00:00:00 2001 From: Gustavo Niemeyer Date: Tue, 1 Mar 2016 17:32:01 -0300 Subject: [PATCH 19/40] Add .travis.yml --- .travis.yml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..004172a2 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,9 @@ +language: go + +go: + - 1.4 + - 1.5 + - 1.6 + - tip + +go_import_path: gopkg.in/yaml.v2 From e4d366fc3c7938e2958e662b4258c7a89e1f0e3e Mon Sep 17 00:00:00 2001 From: Gustavo Niemeyer Date: Fri, 15 Jul 2016 00:37:55 -0300 Subject: [PATCH 20/40] Updated LICENSE to Apache License 2.0. --- LICENSE | 195 +++----------------------------------------------------- 1 file changed, 10 insertions(+), 185 deletions(-) diff --git a/LICENSE b/LICENSE index a68e67f0..866d74a7 100644 --- a/LICENSE +++ b/LICENSE @@ -1,188 +1,13 @@ +Copyright 2011-2016 Canonical Ltd. -Copyright (c) 2011-2014 - Canonical Inc. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at -This software is licensed under the LGPLv3, included below. + http://www.apache.org/licenses/LICENSE-2.0 -As a special exception to the GNU Lesser General Public License version 3 -("LGPL3"), the copyright holders of this Library give you permission to -convey to a third party a Combined Work that links statically or dynamically -to this Library without providing any Minimal Corresponding Source or -Minimal Application Code as set out in 4d or providing the installation -information set out in section 4e, provided that you comply with the other -provisions of LGPL3 and provided that you meet, for the Application the -terms and conditions of the license(s) which apply to the Application. - -Except as stated in this special exception, the provisions of LGPL3 will -continue to comply in full to this Library. If you modify this Library, you -may apply this exception to your version of this Library, but you are not -obliged to do so. If you do not wish to do so, delete this exception -statement from your version. This exception does not (and cannot) modify any -license terms which apply to the Application, with which you must still -comply. - - - GNU LESSER GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - - This version of the GNU Lesser General Public License incorporates -the terms and conditions of version 3 of the GNU General Public -License, supplemented by the additional permissions listed below. - - 0. Additional Definitions. - - As used herein, "this License" refers to version 3 of the GNU Lesser -General Public License, and the "GNU GPL" refers to version 3 of the GNU -General Public License. - - "The Library" refers to a covered work governed by this License, -other than an Application or a Combined Work as defined below. - - An "Application" is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - - A "Combined Work" is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the "Linked -Version". - - The "Minimal Corresponding Source" for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - - The "Corresponding Application Code" for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. - - 1. Exception to Section 3 of the GNU GPL. - - You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. - - 2. Conveying Modified Versions. - - If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - - a) under this License, provided that you make a good faith effort to - ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or - - b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. - - 3. Object Code Incorporating Material from Library Header Files. - - The object code form of an Application may incorporate material from -a header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - - a) Give prominent notice with each copy of the object code that the - Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the object code with a copy of the GNU GPL and this license - document. - - 4. Combined Works. - - You may convey a Combined Work under terms of your choice that, -taken together, effectively do not restrict modification of the -portions of the Library contained in the Combined Work and reverse -engineering for debugging such modifications, if you also do each of -the following: - - a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the Combined Work with a copy of the GNU GPL and this license - document. - - c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. - - d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of this - License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (a) uses at run time - a copy of the Library already present on the user's computer - system, and (b) will operate properly with a modified version - of the Library that is interface-compatible with the Linked - Version. - - e) Provide Installation Information, but only if you would otherwise - be required to provide such information under section 6 of the - GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the - Application with a modified version of the Linked Version. (If - you use option 4d0, the Installation Information must accompany - the Minimal Corresponding Source and Corresponding Application - Code. If you use option 4d1, you must provide the Installation - Information in the manner specified by section 6 of the GNU GPL - for conveying Corresponding Source.) - - 5. Combined Libraries. - - You may place library facilities that are a work based on the -Library side by side in a single library together with other library -facilities that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - - a) Accompany the combined library with a copy of the same work based - on the Library, uncombined with any other library facilities, - conveyed under the terms of this License. - - b) Give prominent notice with the combined library that part of it - is a work based on the Library, and explaining where to find the - accompanying uncombined form of the same work. - - 6. Revised Versions of the GNU Lesser General Public License. - - The Free Software Foundation may publish revised and/or new versions -of the GNU Lesser General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - - Each version is given a distinguishing version number. If the -Library as you received it specifies that a certain numbered version -of the GNU Lesser General Public License "or any later version" -applies to it, you have the option of following the terms and -conditions either of that published version or of any later version -published by the Free Software Foundation. If the Library as you -received it does not specify a version number of the GNU Lesser -General Public License, you may choose any version of the GNU Lesser -General Public License ever published by the Free Software Foundation. - - If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the -Library. +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. From 31c299268d302dd0aa9a0dcf765a3d58971ac83f Mon Sep 17 00:00:00 2001 From: Vasily Korytov Date: Mon, 12 Sep 2016 19:56:03 +0300 Subject: [PATCH 21/40] new license in the README file (#189) New license in the README file. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7b8bd867..1884de6a 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ The package API for yaml v2 will remain stable as described in [gopkg.in](https: License ------- -The yaml package is licensed under the LGPL with an exception that allows it to be linked statically. Please see the LICENSE file for details. +The yaml package is licensed under the Apache License 2.0. Please see the LICENSE file for details. Example From a5b47d31c556af34a302ce5d659e6fea44d90de0 Mon Sep 17 00:00:00 2001 From: Gustavo Niemeyer Date: Wed, 28 Sep 2016 12:37:09 -0300 Subject: [PATCH 22/40] Fix unmarshaler handling of empty strings. --- decode.go | 2 +- decode_test.go | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/decode.go b/decode.go index 085cddc4..b13ab9f0 100644 --- a/decode.go +++ b/decode.go @@ -251,7 +251,7 @@ func (d *decoder) callUnmarshaler(n *node, u Unmarshaler) (good bool) { // // If n holds a null value, prepare returns before doing anything. func (d *decoder) prepare(n *node, out reflect.Value) (newout reflect.Value, unmarshaled, good bool) { - if n.tag == yaml_NULL_TAG || n.kind == scalarNode && n.tag == "" && (n.value == "null" || n.value == "") { + if n.tag == yaml_NULL_TAG || n.kind == scalarNode && n.tag == "" && (n.value == "null" || n.value == "" && n.implicit) { return out, false, false } again := true diff --git a/decode_test.go b/decode_test.go index c159760b..3da6fadf 100644 --- a/decode_test.go +++ b/decode_test.go @@ -660,6 +660,7 @@ var unmarshalerTests = []struct { {`_: BAR!`, "!!str", "BAR!"}, {`_: "BAR!"`, "!!str", "BAR!"}, {"_: !!foo 'BAR!'", "!!foo", "BAR!"}, + {`_: ""`, "!!str", ""}, } var unmarshalerResult = map[int]error{} From 14227de293ca979cf205cd88769fe71ed96a97e2 Mon Sep 17 00:00:00 2001 From: Joe Tsai Date: Tue, 24 Jan 2017 08:25:11 -0800 Subject: [PATCH 23/40] Fix decode test for Go 1.8 (#217) An upstream change (http://golang.org/cl/31144) alters the time package to be more consistent about the representation of UTC location. We adjust the time.Time used in the test to work on both 1.7 and 1.8. --- decode_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/decode_test.go b/decode_test.go index 3da6fadf..5c56a00e 100644 --- a/decode_test.go +++ b/decode_test.go @@ -551,7 +551,7 @@ var unmarshalTests = []struct { }, { "a: 2015-02-24T18:19:39Z\n", - map[string]time.Time{"a": time.Unix(1424801979, 0)}, + map[string]time.Time{"a": time.Unix(1424801979, 0).In(time.UTC)}, }, // Encode empty lists as zero-length slices. From 4c78c975fe7c825c6d1466c42be594d1d6f3aba6 Mon Sep 17 00:00:00 2001 From: Alex Harford Date: Wed, 25 Jan 2017 06:37:19 -0800 Subject: [PATCH 24/40] Tighten restrictions on float decoding (#171) ParseFloat() accepts strings that contain digits with a single 'e' character somewhere in the middle as valid floats. The YAML spec does not accept these. This causes problems especially when dealing with short commit hashes, e.g. `123456e1` --- decode_test.go | 9 +++++++++ resolve.go | 11 ++++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/decode_test.go b/decode_test.go index 5c56a00e..a6fea0f2 100644 --- a/decode_test.go +++ b/decode_test.go @@ -581,6 +581,15 @@ var unmarshalTests = []struct { "\xfe\xff\x00\xf1\x00o\x00\xf1\x00o\x00:\x00 \x00v\x00e\x00r\x00y\x00 \x00y\x00e\x00s\x00 \xd8=\xdf\xd4\x00\n", M{"ñoño": "very yes 🟔"}, }, + + // YAML Float regex shouldn't match this + { + "a: 123456e1\n", + M{"a": "123456e1"}, + }, { + "a: 123456E1\n", + M{"a": "123456E1"}, + }, } type M map[interface{}]interface{} diff --git a/resolve.go b/resolve.go index 93a86327..232313cc 100644 --- a/resolve.go +++ b/resolve.go @@ -3,6 +3,7 @@ package yaml import ( "encoding/base64" "math" + "regexp" "strconv" "strings" "unicode/utf8" @@ -80,6 +81,8 @@ func resolvableTag(tag string) bool { return false } +var yamlStyleFloat = regexp.MustCompile(`^[-+]?[0-9]*\.?[0-9]+([eE][-+][0-9]+)?$`) + func resolve(tag string, in string) (rtag string, out interface{}) { if !resolvableTag(tag) { return tag, in @@ -135,9 +138,11 @@ func resolve(tag string, in string) (rtag string, out interface{}) { if err == nil { return yaml_INT_TAG, uintv } - floatv, err := strconv.ParseFloat(plain, 64) - if err == nil { - return yaml_FLOAT_TAG, floatv + if yamlStyleFloat.MatchString(plain) { + floatv, err := strconv.ParseFloat(plain, 64) + if err == nil { + return yaml_FLOAT_TAG, floatv + } } if strings.HasPrefix(plain, "0b") { intv, err := strconv.ParseInt(plain[2:], 2, 64) From a3f3340b5840cee44f372bddb5880fcbc419b46a Mon Sep 17 00:00:00 2001 From: "Aaron.L.Xu" Date: Wed, 8 Feb 2017 22:18:51 +0800 Subject: [PATCH 25/40] Fix dead URL for yaml specification (#240) --- scannerc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scannerc.go b/scannerc.go index 25808000..2c9d5111 100644 --- a/scannerc.go +++ b/scannerc.go @@ -9,7 +9,7 @@ import ( // ************ // // The following notes assume that you are familiar with the YAML specification -// (http://yaml.org/spec/cvs/current.html). We mostly follow it, although in +// (http://yaml.org/spec/1.2/spec.html). We mostly follow it, although in // some cases we are less restrictive that it requires. // // The process of transforming a YAML stream into a sequence of events is From cd8b52f8269e0feb286dfeef29f8fe4d5b397e0b Mon Sep 17 00:00:00 2001 From: Morgan Jones Date: Fri, 7 Apr 2017 18:21:22 +0100 Subject: [PATCH 26/40] Remove unreachable code to fix go vet (#249) --- decode.go | 1 - emitterc.go | 1 - parserc.go | 1 - 3 files changed, 3 deletions(-) diff --git a/decode.go b/decode.go index b13ab9f0..052ecfcd 100644 --- a/decode.go +++ b/decode.go @@ -120,7 +120,6 @@ func (p *parser) parse() *node { default: panic("attempted to parse unknown event: " + strconv.Itoa(int(p.event.typ))) } - panic("unreachable") } func (p *parser) node(kind int) *node { diff --git a/emitterc.go b/emitterc.go index 2befd553..6ecdcb3c 100644 --- a/emitterc.go +++ b/emitterc.go @@ -666,7 +666,6 @@ func yaml_emitter_emit_node(emitter *yaml_emitter_t, event *yaml_event_t, return yaml_emitter_set_emitter_error(emitter, "expected SCALAR, SEQUENCE-START, MAPPING-START, or ALIAS") } - return false } // Expect ALIAS. diff --git a/parserc.go b/parserc.go index 0a7037ad..81d05dfe 100644 --- a/parserc.go +++ b/parserc.go @@ -166,7 +166,6 @@ func yaml_parser_state_machine(parser *yaml_parser_t, event *yaml_event_t) bool default: panic("invalid parser state") } - return false } // Parse the production: From f59f5e67022f3c186e20af01b1993b86ac74f0dc Mon Sep 17 00:00:00 2001 From: Bill Q Date: Tue, 27 Jun 2017 02:36:45 +0300 Subject: [PATCH 27/40] fix misspell on emmiterc.go --- emitterc.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/emitterc.go b/emitterc.go index 6ecdcb3c..41de8b85 100644 --- a/emitterc.go +++ b/emitterc.go @@ -994,7 +994,7 @@ func yaml_emitter_analyze_scalar(emitter *yaml_emitter_t, value []byte) bool { break_space = false space_break = false - preceeded_by_whitespace = false + preceded_by_whitespace = false followed_by_whitespace = false previous_space = false previous_break = false @@ -1016,7 +1016,7 @@ func yaml_emitter_analyze_scalar(emitter *yaml_emitter_t, value []byte) bool { flow_indicators = true } - preceeded_by_whitespace = true + preceded_by_whitespace = true for i, w := 0, 0; i < len(value); i += w { w = width(value[i]) followed_by_whitespace = i+w >= len(value) || is_blank(value, i+w) @@ -1047,7 +1047,7 @@ func yaml_emitter_analyze_scalar(emitter *yaml_emitter_t, value []byte) bool { block_indicators = true } case '#': - if preceeded_by_whitespace { + if preceded_by_whitespace { flow_indicators = true block_indicators = true } @@ -1088,7 +1088,7 @@ func yaml_emitter_analyze_scalar(emitter *yaml_emitter_t, value []byte) bool { } // [Go]: Why 'z'? Couldn't be the end of the string as that's the loop condition. - preceeded_by_whitespace = is_blankz(value, i) + preceded_by_whitespace = is_blankz(value, i) } emitter.scalar_data.multiline = line_breaks From e72f93569ef83aca933836c2fb9185faeeced236 Mon Sep 17 00:00:00 2001 From: Bill Q Date: Tue, 27 Jun 2017 02:37:29 +0300 Subject: [PATCH 28/40] correct misspell on yamlh.go --- yamlh.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yamlh.go b/yamlh.go index d60a6b6b..3caeca04 100644 --- a/yamlh.go +++ b/yamlh.go @@ -508,7 +508,7 @@ type yaml_parser_t struct { problem string // Error description. - // The byte about which the problem occured. + // The byte about which the problem occurred. problem_offset int problem_value int problem_mark yaml_mark_t From f8db564a0a4a5f6d04f66522493597f18e5ab4ae Mon Sep 17 00:00:00 2001 From: Witold Krecicki Date: Tue, 13 Jun 2017 12:23:14 +0200 Subject: [PATCH 29/40] Add UnmarshalStrict returning error if yaml has fields that do not exist in structure --- decode.go | 7 +++++-- decode_test.go | 11 +++++++++++ yaml.go | 13 ++++++++++++- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/decode.go b/decode.go index 052ecfcd..db1f5f20 100644 --- a/decode.go +++ b/decode.go @@ -190,6 +190,7 @@ type decoder struct { aliases map[string]bool mapType reflect.Type terrors []string + strict bool } var ( @@ -199,8 +200,8 @@ var ( ifaceType = defaultMapType.Elem() ) -func newDecoder() *decoder { - d := &decoder{mapType: defaultMapType} +func newDecoder(strict bool) *decoder { + d := &decoder{mapType: defaultMapType, strict: strict} d.aliases = make(map[string]bool) return d } @@ -639,6 +640,8 @@ func (d *decoder) mappingStruct(n *node, out reflect.Value) (good bool) { value := reflect.New(elemType).Elem() d.unmarshal(n.children[i+1], value) inlineMap.SetMapIndex(name, value) + } else if d.strict { + d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s not found in struct %s", n.line+1, name.String(), out.Type())) } } return true diff --git a/decode_test.go b/decode_test.go index a6fea0f2..3d0807a0 100644 --- a/decode_test.go +++ b/decode_test.go @@ -968,6 +968,17 @@ func (s *S) TestUnmarshalSliceOnPreset(c *C) { c.Assert(v.A, DeepEquals, []int{2}) } +func (s *S) TestUnmarshalStrict(c *C) { + v := struct{ A, B int }{} + + err := yaml.UnmarshalStrict([]byte("a: 1\nb: 2"), &v) + c.Check(err, IsNil) + err = yaml.Unmarshal([]byte("a: 1\nb: 2\nc: 3"), &v) + c.Check(err, IsNil) + err = yaml.UnmarshalStrict([]byte("a: 1\nb: 2\nc: 3"), &v) + c.Check(err, ErrorMatches, "yaml: unmarshal errors:\n line 1: field c not found in struct struct { A int; B int }") +} + //var data []byte //func init() { // var err error diff --git a/yaml.go b/yaml.go index 36d6b883..bf18884e 100644 --- a/yaml.go +++ b/yaml.go @@ -77,8 +77,19 @@ type Marshaler interface { // supported tag options. // func Unmarshal(in []byte, out interface{}) (err error) { + return unmarshal(in, out, false) +} + +// UnmarshalStrict is like Unmarshal except that any fields that are found +// in the data that do not have corresponding struct members will result in +// an error. +func UnmarshalStrict(in []byte, out interface{}) (err error) { + return unmarshal(in, out, true) +} + +func unmarshal(in []byte, out interface{}, strict bool) (err error) { defer handleErr(&err) - d := newDecoder() + d := newDecoder(strict) p := newParser(in) defer p.destroy() node := p.parse() From d56ec34a3ded0bb58c82198664664ccb81eec91b Mon Sep 17 00:00:00 2001 From: Roger Peppe Date: Fri, 21 Jul 2017 10:26:57 +0100 Subject: [PATCH 30/40] move embedded struct example into godoc Examples are nicer when they're shown in the godoc. --- example_embedded_test.go | 41 ++++++++++++++++++++++++++++++++++++ examples/embedded-structs.go | 35 ------------------------------ 2 files changed, 41 insertions(+), 35 deletions(-) create mode 100644 example_embedded_test.go delete mode 100644 examples/embedded-structs.go diff --git a/example_embedded_test.go b/example_embedded_test.go new file mode 100644 index 00000000..c8b241d5 --- /dev/null +++ b/example_embedded_test.go @@ -0,0 +1,41 @@ +package yaml_test + +import ( + "fmt" + "log" + + "gopkg.in/yaml.v2" +) + +// An example showing how to unmarshal embedded +// structs from YAML. + +type StructA struct { + A string `yaml:"a"` +} + +type StructB struct { + // Embedded structs are not treated as embedded in YAML by default. To do that, + // add the ",inline" annotation below + StructA `yaml:",inline"` + B string `yaml:"b"` +} + +var data = ` +a: a string from struct A +b: a string from struct B +` + +func ExampleUnmarshal_embedded() { + var b StructB + + err := yaml.Unmarshal([]byte(data), &b) + if err != nil { + log.Fatal("cannot unmarshal data: %v", err) + } + fmt.Println(b.A) + fmt.Println(b.B) + // Output: + // a string from struct A + // a string from struct B +} diff --git a/examples/embedded-structs.go b/examples/embedded-structs.go deleted file mode 100644 index 307ab8fa..00000000 --- a/examples/embedded-structs.go +++ /dev/null @@ -1,35 +0,0 @@ -package main - -import ( - "fmt" - "log" - - "gopkg.in/yaml.v2" -) - -type StructA struct { - A string `yaml:"a"` -} - -type StructB struct { - // go-yaml will not decode embedded structs by default, to do that - // you need to add the ",inline" annotation below - StructA `yaml:",inline"` - B string `yaml:"b"` -} - -var data = ` -a: a string from struct A -b: a string from struct B -` - -func main() { - var b StructB - - err := yaml.Unmarshal([]byte(data), &b) - if err != nil { - log.Fatalf("error: %v", err) - } - fmt.Println(b.A) - fmt.Println(b.B) -} From e9bfed595636e952566e5cb857c22b918f2530a2 Mon Sep 17 00:00:00 2001 From: Roger Peppe Date: Fri, 21 Jul 2017 12:37:23 +0100 Subject: [PATCH 31/40] Make tag scanning code slightly cleaner. The current code uses "length" as a proxy for "has a tag" but the length doesn't actually match the length of the tag when there's a %-encoded character, so use a boolean instead. Also fix a panic found when trying to write a test for the change. Aside: seems that %-encoded tag handles are not allowed by the YAML spec. --- decode_test.go | 4 +++- scannerc.go | 11 +++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/decode_test.go b/decode_test.go index 3b96b259..713b1ee9 100644 --- a/decode_test.go +++ b/decode_test.go @@ -610,7 +610,8 @@ type inlineC struct { } func (s *S) TestUnmarshal(c *C) { - for _, item := range unmarshalTests { + for i, item := range unmarshalTests { + c.Logf("test %d: %q", i, item.data) t := reflect.ValueOf(item.value).Type() var value interface{} switch t.Kind() { @@ -654,6 +655,7 @@ var unmarshalErrorTests = []struct { {"a: !!binary ==", "yaml: !!binary value contains invalid base64 data"}, {"{[.]}", `yaml: invalid map key: \[\]interface \{\}\{"\."\}`}, {"{{.}}", `yaml: invalid map key: map\[interface\ \{\}\]interface \{\}\{".":interface \{\}\(nil\)\}`}, + {"%TAG !%79! tag:yaml.org,2002:\n---\nv: !%79!int '1'", "yaml: did not find expected whitespace"}, } func (s *S) TestUnmarshalErrors(c *C) { diff --git a/scannerc.go b/scannerc.go index dd64ee4b..07448445 100644 --- a/scannerc.go +++ b/scannerc.go @@ -1944,7 +1944,7 @@ func yaml_parser_scan_tag_handle(parser *yaml_parser_t, directive bool, start_ma } else { // It's either the '!' tag or not really a tag handle. If it's a %TAG // directive, it's an error. If it's a tag token, it must be a part of URI. - if directive && !(s[0] == '!' && s[1] == 0) { + if directive && string(s) != "!" { yaml_parser_set_scanner_tag_error(parser, directive, start_mark, "did not find expected '!'") return false @@ -1959,12 +1959,12 @@ func yaml_parser_scan_tag_handle(parser *yaml_parser_t, directive bool, start_ma func yaml_parser_scan_tag_uri(parser *yaml_parser_t, directive bool, head []byte, start_mark yaml_mark_t, uri *[]byte) bool { //size_t length = head ? strlen((char *)head) : 0 var s []byte - length := len(head) + hasTag := len(head) > 0 // Copy the head if needed. // // Note that we don't copy the leading '!' character. - if length > 0 { + if len(head) > 1 { s = append(s, head[1:]...) } @@ -1997,15 +1997,14 @@ func yaml_parser_scan_tag_uri(parser *yaml_parser_t, directive bool, head []byte } } else { s = read(parser, s) - length++ } if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } + hasTag = true } - // Check if the tag is non-empty. - if length == 0 { + if !hasTag { yaml_parser_set_scanner_tag_error(parser, directive, start_mark, "did not find expected tag URI") return false From eb3733d160e74a9c7e442f435eb3bea458e1d19f Mon Sep 17 00:00:00 2001 From: James King Date: Sat, 12 Aug 2017 12:00:11 -0400 Subject: [PATCH 32/40] Replace LICENSE text with actual license (#274) --- LICENSE | 208 +++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 198 insertions(+), 10 deletions(-) diff --git a/LICENSE b/LICENSE index 866d74a7..8dada3ed 100644 --- a/LICENSE +++ b/LICENSE @@ -1,13 +1,201 @@ -Copyright 2011-2016 Canonical Ltd. + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - http://www.apache.org/licenses/LICENSE-2.0 + 1. Definitions. -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. From bd18fbd806e8a0c6cda3f7d6a8f19d93770c7a50 Mon Sep 17 00:00:00 2001 From: Vinzenz Feenstra Date: Tue, 1 Nov 2016 12:56:18 +0100 Subject: [PATCH 33/40] Make aliases share the same memory address as the anchor ( go-yaml/yaml#215 ) Signed-off-by: Vinzenz Feenstra --- decode.go | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/decode.go b/decode.go index db1f5f20..1984d930 100644 --- a/decode.go +++ b/decode.go @@ -26,6 +26,7 @@ type node struct { implicit bool children []*node anchors map[string]*node + data interface{} } // ---------------------------------------------------------------------------- @@ -277,12 +278,18 @@ func (d *decoder) prepare(n *node, out reflect.Value) (newout reflect.Value, unm func (d *decoder) unmarshal(n *node, out reflect.Value) (good bool) { switch n.kind { case documentNode: - return d.document(n, out) + if good := d.document(n, out); good { + n.data = out.Addr().Interface() + } + return good case aliasNode: return d.alias(n, out) } out, unmarshaled, good := d.prepare(n, out) if unmarshaled { + if good { + n.data = out.Addr().Interface() + } return good } switch n.kind { @@ -295,6 +302,9 @@ func (d *decoder) unmarshal(n *node, out reflect.Value) (good bool) { default: panic("internal error: unknown node kind: " + strconv.Itoa(n.kind)) } + if good { + n.data = out.Addr().Interface() + } return good } @@ -312,13 +322,26 @@ func (d *decoder) alias(n *node, out reflect.Value) (good bool) { if !ok { failf("unknown anchor '%s' referenced", n.value) } - if d.aliases[n.value] { - failf("anchor '%s' value contains itself", n.value) + + if d.aliases[n.value] && out.Kind() != reflect.Ptr { + failf("anchor '%s' value contains itself and is not a pointer", n.value) + } else if d.aliases[n.value] && out.Kind() == reflect.Ptr { + out.Set(an.data.(reflect.Value).Elem()) + } else { + d.aliases[n.value] = true + if an.data == nil { + an.data = out.Addr() + good = d.unmarshal(an, out) + } else { + if out.Kind() == reflect.Ptr { + out.Set(reflect.ValueOf(an.data)) + } else { + out.Set(reflect.ValueOf(an.data).Elem()) + } + } + delete(d.aliases, n.value) } - d.aliases[n.value] = true - good = d.unmarshal(an, out) - delete(d.aliases, n.value) - return good + return } var zeroValue reflect.Value From 78ad01d05c5a5beb9dd15cae14431ffd394c01d6 Mon Sep 17 00:00:00 2001 From: Vinzenz Feenstra Date: Tue, 1 Nov 2016 13:06:21 +0100 Subject: [PATCH 34/40] Removed introduced shadowing bug --- decode.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/decode.go b/decode.go index 1984d930..8633e1d8 100644 --- a/decode.go +++ b/decode.go @@ -278,7 +278,7 @@ func (d *decoder) prepare(n *node, out reflect.Value) (newout reflect.Value, unm func (d *decoder) unmarshal(n *node, out reflect.Value) (good bool) { switch n.kind { case documentNode: - if good := d.document(n, out); good { + if good = d.document(n, out); good { n.data = out.Addr().Interface() } return good From 1f81cdb8034b3b81841bfe03f20e97dac3064048 Mon Sep 17 00:00:00 2001 From: Vinzenz Feenstra Date: Wed, 2 Nov 2016 16:50:13 +0100 Subject: [PATCH 35/40] Applied API changes as suggested in another PR and fixed outstanding problems Signed-off-by: Vinzenz Feenstra --- decode.go | 109 +++++++++++++++++++++++++++++++++++++++++++----------- yaml.go | 2 +- 2 files changed, 88 insertions(+), 23 deletions(-) diff --git a/decode.go b/decode.go index 8633e1d8..8c248582 100644 --- a/decode.go +++ b/decode.go @@ -186,12 +186,63 @@ func (p *parser) mapping() *node { // ---------------------------------------------------------------------------- // Decoder, unmarshals a node into a provided value. +// Decoder unmarshals a node into a provided value. +type Decoder struct { + *decoder +} + +type DecoderOptions struct { + Strict bool + Recursive bool +} + +// NewDecoder creates and initializes a new Decoder struct. +func NewDecoder() *Decoder { + return &Decoder{ + newDecoder(), + } +} + +// NewStrictDecoder creates and initializes a new Decoder with strict enabled. +func NewStrictDecoder() *Decoder { + return NewDecoderWithOptions(DecoderOptions{ + Strict: true, + }) +} + +// NewDecoderWithOptions creates and initializes a new Decoder and sets the passed options +func NewDecoderWithOptions(options DecoderOptions) *Decoder { + d := NewDecoder() + d.SetOptions(options) + return d +} + +// SetStrict puts the decoder to strict mode +func (d *Decoder) SetStrict(strict bool) { + d.options.Strict = strict +} + +// SetAllowRecursive allows to enable / disable the recursive parsing feature +func (d *Decoder) SetAllowRecursive(recursive bool) { + d.options.Recursive = recursive +} + +// SetOptions allows to set all options at once +func (d *Decoder) SetOptions(options DecoderOptions) { + d.options = options +} + +// GetOptions returns the current options +func (d Decoder) GetOptions() DecoderOptions { + return d.options +} + type decoder struct { doc *node aliases map[string]bool mapType reflect.Type terrors []string - strict bool + options DecoderOptions } var ( @@ -201,8 +252,8 @@ var ( ifaceType = defaultMapType.Elem() ) -func newDecoder(strict bool) *decoder { - d := &decoder{mapType: defaultMapType, strict: strict} +func newDecoder() *decoder { + d := &decoder{mapType: defaultMapType} d.aliases = make(map[string]bool) return d } @@ -279,7 +330,7 @@ func (d *decoder) unmarshal(n *node, out reflect.Value) (good bool) { switch n.kind { case documentNode: if good = d.document(n, out); good { - n.data = out.Addr().Interface() + n.data = out } return good case aliasNode: @@ -287,9 +338,7 @@ func (d *decoder) unmarshal(n *node, out reflect.Value) (good bool) { } out, unmarshaled, good := d.prepare(n, out) if unmarshaled { - if good { - n.data = out.Addr().Interface() - } + n.data = out return good } switch n.kind { @@ -299,11 +348,12 @@ func (d *decoder) unmarshal(n *node, out reflect.Value) (good bool) { good = d.mapping(n, out) case sequenceNode: good = d.sequence(n, out) + out = out.Addr() default: panic("internal error: unknown node kind: " + strconv.Itoa(n.kind)) } if good { - n.data = out.Addr().Interface() + n.data = out } return good } @@ -323,24 +373,39 @@ func (d *decoder) alias(n *node, out reflect.Value) (good bool) { failf("unknown anchor '%s' referenced", n.value) } - if d.aliases[n.value] && out.Kind() != reflect.Ptr { - failf("anchor '%s' value contains itself and is not a pointer", n.value) - } else if d.aliases[n.value] && out.Kind() == reflect.Ptr { - out.Set(an.data.(reflect.Value).Elem()) - } else { - d.aliases[n.value] = true - if an.data == nil { - an.data = out.Addr() - good = d.unmarshal(an, out) + if d.options.Recursive { + if d.aliases[n.value] && out.Kind() != reflect.Ptr { + failf("anchor '%s' value contains itself and is not a pointer", n.value) + } else if d.aliases[n.value] && out.Kind() == reflect.Ptr { + out.Set(an.data.(reflect.Value).Elem()) } else { - if out.Kind() == reflect.Ptr { - out.Set(reflect.ValueOf(an.data)) + d.aliases[n.value] = true + if an.data == nil { + an.data = out.Addr() + good = d.unmarshal(an, out) } else { - out.Set(reflect.ValueOf(an.data).Elem()) + if _, ok := an.data.(reflect.Value); !ok { + an.data = reflect.ValueOf(an.data) + } + if out.Kind() == reflect.Ptr { + out.Set(an.data.(reflect.Value).Addr()) + } else if data := an.data.(reflect.Value); data.Kind() == reflect.Ptr { + out.Set(data.Elem()) + } else { + good = d.unmarshal(an, out) + } } + delete(d.aliases, n.value) + } + } else { + if d.aliases[n.value] { + failf("anchor '%s' value contains itself", n.value) } + d.aliases[n.value] = true + good = d.unmarshal(an, out) delete(d.aliases, n.value) } + return } @@ -663,11 +728,11 @@ func (d *decoder) mappingStruct(n *node, out reflect.Value) (good bool) { value := reflect.New(elemType).Elem() d.unmarshal(n.children[i+1], value) inlineMap.SetMapIndex(name, value) - } else if d.strict { + } else if d.options.Strict { d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s not found in struct %s", n.line+1, name.String(), out.Type())) } } - return true + return len(d.terrors) == 0 } func failWantMap() { diff --git a/yaml.go b/yaml.go index bf18884e..f2177a64 100644 --- a/yaml.go +++ b/yaml.go @@ -89,7 +89,7 @@ func UnmarshalStrict(in []byte, out interface{}) (err error) { func unmarshal(in []byte, out interface{}, strict bool) (err error) { defer handleErr(&err) - d := newDecoder(strict) + d := NewDecoderWithOptions(DecoderOptions{Strict: strict}) p := newParser(in) defer p.destroy() node := p.parse() From 1cd994217c9019db21976dcfca50baa684b053bb Mon Sep 17 00:00:00 2001 From: Vinzenz Feenstra Date: Wed, 2 Nov 2016 19:15:07 +0100 Subject: [PATCH 36/40] Always use the pointer mechanism, but only allow recursion per option --- decode.go | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/decode.go b/decode.go index 8c248582..17a5440f 100644 --- a/decode.go +++ b/decode.go @@ -373,36 +373,38 @@ func (d *decoder) alias(n *node, out reflect.Value) (good bool) { failf("unknown anchor '%s' referenced", n.value) } + handled := false if d.options.Recursive { if d.aliases[n.value] && out.Kind() != reflect.Ptr { failf("anchor '%s' value contains itself and is not a pointer", n.value) } else if d.aliases[n.value] && out.Kind() == reflect.Ptr { out.Set(an.data.(reflect.Value).Elem()) + handled = true } else { - d.aliases[n.value] = true - if an.data == nil { - an.data = out.Addr() - good = d.unmarshal(an, out) - } else { - if _, ok := an.data.(reflect.Value); !ok { - an.data = reflect.ValueOf(an.data) - } - if out.Kind() == reflect.Ptr { - out.Set(an.data.(reflect.Value).Addr()) - } else if data := an.data.(reflect.Value); data.Kind() == reflect.Ptr { - out.Set(data.Elem()) - } else { - good = d.unmarshal(an, out) - } - } - delete(d.aliases, n.value) } } else { if d.aliases[n.value] { failf("anchor '%s' value contains itself", n.value) } + } + + if !handled { d.aliases[n.value] = true - good = d.unmarshal(an, out) + if an.data == nil { + an.data = out.Addr() + good = d.unmarshal(an, out) + } else { + if _, ok := an.data.(reflect.Value); !ok { + an.data = reflect.ValueOf(an.data) + } + if out.Kind() == reflect.Ptr { + out.Set(an.data.(reflect.Value).Addr()) + } else if data := an.data.(reflect.Value); data.Kind() == reflect.Ptr { + out.Set(data.Elem()) + } else { + good = d.unmarshal(an, out) + } + } delete(d.aliases, n.value) } From a0910c5ff412ba6db8c98f6aa560aae4fd70c2f7 Mon Sep 17 00:00:00 2001 From: Vinzenz Feenstra Date: Wed, 2 Nov 2016 20:15:06 +0100 Subject: [PATCH 37/40] Fix for issue go-yaml/yaml#144 Signed-off-by: Vinzenz Feenstra --- encode.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/encode.go b/encode.go index 84f84995..a98c688a 100644 --- a/encode.go +++ b/encode.go @@ -99,7 +99,7 @@ func (e *encoder) marshal(tag string, in reflect.Value) { } case reflect.Struct: e.structv(tag, in) - case reflect.Slice: + case reflect.Slice, reflect.Array: if in.Type().Elem() == mapItemType { e.itemsv(tag, in) } else { From 759b7ad4b8ebab321b4d030201ad4328e9a692ee Mon Sep 17 00:00:00 2001 From: Vinzenz Feenstra Date: Wed, 2 Nov 2016 20:48:45 +0100 Subject: [PATCH 38/40] Fixes go-yaml/yaml#214 - New option to allow setting strict boolean mode Signed-off-by: Vinzenz Feenstra --- decode.go | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/decode.go b/decode.go index 17a5440f..7da6c457 100644 --- a/decode.go +++ b/decode.go @@ -192,8 +192,9 @@ type Decoder struct { } type DecoderOptions struct { - Strict bool - Recursive bool + Strict bool + Recursive bool + StrictBool bool } // NewDecoder creates and initializes a new Decoder struct. @@ -222,6 +223,11 @@ func (d *Decoder) SetStrict(strict bool) { d.options.Strict = strict } +// SetStrict puts the decoder to strict boolean mode (According to the 1.2 YAML Spec) +func (d *Decoder) SetStrictBool(strict bool) { + d.options.StrictBool = strict +} + // SetAllowRecursive allows to enable / disable the recursive parsing feature func (d *Decoder) SetAllowRecursive(recursive bool) { d.options.Recursive = recursive @@ -433,6 +439,10 @@ func (d *decoder) scalar(n *node, out reflect.Value) (good bool) { failf("!!binary value contains invalid base64 data") } resolved = string(data) + } else if d.options.StrictBool && tag == yaml_BOOL_TAG { + if resolved != "true" && resolved != "false" { + tag = yaml_STR_TAG + } } } if resolved == nil { From 764aa8c1729663c83a7df36e1a68e81ca32ce8f1 Mon Sep 17 00:00:00 2001 From: Vinzenz Feenstra Date: Wed, 2 Nov 2016 19:54:31 +0100 Subject: [PATCH 39/40] Fix for issue go-yaml/yaml#91 Signed-off-by: Vinzenz Feenstra --- decode.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/decode.go b/decode.go index 7da6c457..9281293d 100644 --- a/decode.go +++ b/decode.go @@ -683,7 +683,11 @@ func (d *decoder) mappingSlice(n *node, out reflect.Value) (good bool) { var l = len(n.children) for i := 0; i < l; i += 2 { if isMerge(n.children[i]) { - d.merge(n.children[i+1], out) + tmp := reflect.ValueOf(map[interface{}]interface{}{}) + d.merge(n.children[i+1], tmp) + for k, v := range tmp.Interface().(map[interface{}]interface{}) { + slice = append(slice, MapItem{k, v}) + } continue } item := MapItem{} From 91409cdd725d9e2d1be34f7aa7f0992a469cd678 Mon Sep 17 00:00:00 2001 From: Vinzenz Feenstra Date: Thu, 3 Nov 2016 10:02:47 +0100 Subject: [PATCH 40/40] Add test cases from go-yaml/yaml#184 Signed-off-by: Vinzenz Feenstra --- decode_test.go | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/decode_test.go b/decode_test.go index 713b1ee9..a31875d4 100644 --- a/decode_test.go +++ b/decode_test.go @@ -945,6 +945,50 @@ func (s *S) TestMergeStruct(c *C) { } } +func (s *S) TestMapMerging(c *C) { + data := `--- +world: &world + greeting: Hello +earth: + << : *world` + + config := map[string]interface{}{} + err := yaml.Unmarshal([]byte(data), &config) + if err != nil { + c.Errorf("Failed to parse\n%v\n\ninto a %T: %v", data, config, err) + } + + earth := config["earth"] + expected := map[interface{}]interface{}{ + "greeting": "Hello", + } + + if !reflect.DeepEqual(earth, expected) { + c.Errorf("Merging does not work. Expected %v (%T), got %v (%T)", expected, expected, earth, earth) + } +} + +func (s *S) TestMapSliceMerging(c *C) { + data := `--- +world: &world + greeting: Hello +earth: + << : *world` + + config := make(yaml.MapSlice, 0) + err := yaml.Unmarshal([]byte(data), &config) + if err != nil { + c.Errorf("Failed to parse\n%v\n\ninto a %T: %v", data, config, err) + } + + earth := config[1].Value + expected := yaml.MapSlice{{Key: "greeting", Value: "Hello"}} + + if !reflect.DeepEqual(earth, expected) { + c.Errorf("Merging does not work. Expected %v (%T), got %v (%T)", expected, expected, earth, earth) + } +} + var unmarshalNullTests = []func() interface{}{ func() interface{} { var v interface{}; v = "v"; return &v }, func() interface{} { var s = "s"; return &s },