Skip to content

Commit

Permalink
Merge remote-tracking branch 'v/fix-for-issue-91' into v2
Browse files Browse the repository at this point in the history
* v/fix-for-issue-91: (40 commits)
  Add test cases from go-yaml#184
  Fix for issue go-yaml#91
  Fixes go-yaml#214 - New option to allow setting strict boolean mode
  Fix for issue go-yaml#144
  Always use the pointer mechanism, but only allow recursion per option
  Applied API changes as suggested in another PR and fixed outstanding problems
  Removed introduced shadowing bug
  Make aliases share the same memory address as the anchor ( go-yaml#215 )
  Replace LICENSE text with actual license (go-yaml#274)
  Make tag scanning code slightly cleaner.
  move embedded struct example into godoc
  Add UnmarshalStrict returning error if yaml has fields that do not exist in structure
  correct misspell on yamlh.go
  fix misspell on emmiterc.go
  Remove unreachable code to fix go vet (go-yaml#249)
  Fix dead URL for yaml specification (go-yaml#240)
  Tighten restrictions on float decoding (go-yaml#171)
  Fix decode test for Go 1.8 (go-yaml#217)
  Fix unmarshaler handling of empty strings.
  new license in the README file (go-yaml#189)
  ...
  • Loading branch information
laszlocph committed Nov 14, 2019
2 parents 5d6f7e0 + 91409cd commit 2ec4ce7
Show file tree
Hide file tree
Showing 15 changed files with 560 additions and 244 deletions.
9 changes: 9 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
language: go

go:
- 1.4
- 1.5
- 1.6
- tip

go_import_path: gopkg.in/yaml.v2
389 changes: 201 additions & 188 deletions LICENSE

Large diffs are not rendered by default.

9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,14 @@ 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
-------

Some more examples can be found in the "examples" folder.

```Go
package main

Expand All @@ -67,7 +69,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() {
Expand Down
128 changes: 117 additions & 11 deletions decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type node struct {
implicit bool
children []*node
anchors map[string]*node
data interface{}
}

// ----------------------------------------------------------------------------
Expand Down Expand Up @@ -120,7 +121,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 {
Expand Down Expand Up @@ -186,11 +186,69 @@ 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
StrictBool 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
}

// 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
}

// 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
options DecoderOptions
}

var (
Expand Down Expand Up @@ -251,7 +309,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
Expand All @@ -277,12 +335,16 @@ 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
}
return good
case aliasNode:
return d.alias(n, out)
}
out, unmarshaled, good := d.prepare(n, out)
if unmarshaled {
n.data = out
return good
}
switch n.kind {
Expand All @@ -292,9 +354,13 @@ 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
}
return good
}

Expand All @@ -312,13 +378,43 @@ 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)

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 {
}
} 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 good

if !handled {
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)
}

return
}

var zeroValue reflect.Value
Expand All @@ -343,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 {
Expand Down Expand Up @@ -583,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{}
Expand Down Expand Up @@ -640,9 +744,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.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() {
Expand Down
Loading

0 comments on commit 2ec4ce7

Please sign in to comment.