From c03d57b57898871229d4b950cf04eb8bf7e225b0 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 25 Oct 2016 10:44:39 -0700 Subject: [PATCH] json/parser: empty list value should not flatten Fixes https://github.com/hashicorp/terraform/issues/8886 While parsing JSON, an empty list value would be assumed to be a non-existent list of objects, and would be removed from the result. This may have never been the correct behavior but always worked okay because we previously didn't support lists as first class types. With the support of lists, we need to actually return the empty list as the type. If we return nothing, then projects like Terraform will think that the value was never set, which is false. --- decoder_test.go | 8 +++++ json/parser/flatten.go | 6 ++++ json/parser/parser.go | 1 + json/parser/parser_test.go | 42 ++++++++++++++++++------- test-fixtures/structure_list_empty.json | 3 ++ 5 files changed, 48 insertions(+), 12 deletions(-) create mode 100644 test-fixtures/structure_list_empty.json diff --git a/decoder_test.go b/decoder_test.go index 5a8404c..d8272a0 100644 --- a/decoder_test.go +++ b/decoder_test.go @@ -274,6 +274,14 @@ func TestDecode_interface(t *testing.T) { }, }, + { + "structure_list_empty.json", + false, + map[string]interface{}{ + "foo": []interface{}{}, + }, + }, + { "nested_block_comment.hcl", false, diff --git a/json/parser/flatten.go b/json/parser/flatten.go index 6eb14a2..f652d6f 100644 --- a/json/parser/flatten.go +++ b/json/parser/flatten.go @@ -48,6 +48,12 @@ func flattenListType( item *ast.ObjectItem, items []*ast.ObjectItem, frontier []*ast.ObjectItem) ([]*ast.ObjectItem, []*ast.ObjectItem) { + // If the list is empty, keep the original list + if len(ot.List) == 0 { + items = append(items, item) + return items, frontier + } + // All the elements of this object must also be objects! for _, subitem := range ot.List { if _, ok := subitem.(*ast.ObjectType); !ok { diff --git a/json/parser/parser.go b/json/parser/parser.go index acf9594..6f46085 100644 --- a/json/parser/parser.go +++ b/json/parser/parser.go @@ -86,6 +86,7 @@ func (p *Parser) objectList() (*ast.ObjectList, error) { break } } + return node, nil } diff --git a/json/parser/parser_test.go b/json/parser/parser_test.go index 48bba84..e0cebf5 100644 --- a/json/parser/parser_test.go +++ b/json/parser/parser_test.go @@ -186,13 +186,13 @@ func TestFlattenObjects(t *testing.T) { }{ { `{ - "foo": [ - { - "foo": "svh", - "bar": "fatih" - } - ] - }`, + "foo": [ + { + "foo": "svh", + "bar": "fatih" + } + ] + }`, []ast.Node{ &ast.ObjectType{}, &ast.LiteralType{}, @@ -202,15 +202,33 @@ func TestFlattenObjects(t *testing.T) { }, { `{ - "variable": { - "foo": {} - } - }`, + "variable": { + "foo": {} + } + }`, []ast.Node{ &ast.ObjectType{}, }, 1, }, + { + `{ + "empty": [] + }`, + []ast.Node{ + &ast.ListType{}, + }, + 1, + }, + { + `{ + "basic": [1, 2, 3] + }`, + []ast.Node{ + &ast.ListType{}, + }, + 1, + }, } for _, l := range literals { @@ -360,7 +378,7 @@ func TestParse_inline(t *testing.T) { func equals(tb testing.TB, exp, act interface{}) { if !reflect.DeepEqual(exp, act) { _, file, line, _ := runtime.Caller(1) - fmt.Printf("\033[31m%s:%d:\n\n\texp: %#v\n\n\tgot: %#v\033[39m\n\n", filepath.Base(file), line, exp, act) + fmt.Printf("\033[31m%s:%d:\n\n\texp: %s\n\n\tgot: %s\033[39m\n\n", filepath.Base(file), line, exp, act) tb.FailNow() } } diff --git a/test-fixtures/structure_list_empty.json b/test-fixtures/structure_list_empty.json new file mode 100644 index 0000000..d99606f --- /dev/null +++ b/test-fixtures/structure_list_empty.json @@ -0,0 +1,3 @@ +{ + "foo": [] +}