diff --git a/pkg/yqlib/lib.go b/pkg/yqlib/lib.go index ef4be5fd..dd328dcf 100644 --- a/pkg/yqlib/lib.go +++ b/pkg/yqlib/lib.go @@ -352,6 +352,13 @@ func parseInt(numberString string) (int, error) { return int(parsed), err } +func createStringScalarNode(stringValue string) *yaml.Node { + var node = &yaml.Node{Kind: yaml.ScalarNode} + node.Value = stringValue + node.Tag = "!!str" + return node +} + func createScalarNode(value interface{}, stringValue string) *yaml.Node { var node = &yaml.Node{Kind: yaml.ScalarNode} node.Value = stringValue diff --git a/pkg/yqlib/operator_entries.go b/pkg/yqlib/operator_entries.go index c7ab6351..2af9fe94 100644 --- a/pkg/yqlib/operator_entries.go +++ b/pkg/yqlib/operator_entries.go @@ -72,20 +72,20 @@ func parseEntry(entry *yaml.Node, position int) (*yaml.Node, *yaml.Node, error) prefs := traversePreferences{DontAutoCreate: true} candidateNode := &CandidateNode{Node: entry} - keyResults, err := traverseMap(Context{}, candidateNode, "key", prefs, false) + keyResults, err := traverseMap(Context{}, candidateNode, createStringScalarNode("key"), prefs, false) if err != nil { return nil, nil, err } else if keyResults.Len() != 1 { - return nil, nil, fmt.Errorf("Expected to find one 'key' entry but found %v in position %v", keyResults.Len(), position) + return nil, nil, fmt.Errorf("expected to find one 'key' entry but found %v in position %v", keyResults.Len(), position) } - valueResults, err := traverseMap(Context{}, candidateNode, "value", prefs, false) + valueResults, err := traverseMap(Context{}, candidateNode, createStringScalarNode("value"), prefs, false) if err != nil { return nil, nil, err } else if valueResults.Len() != 1 { - return nil, nil, fmt.Errorf("Expected to find one 'value' entry but found %v in position %v", valueResults.Len(), position) + return nil, nil, fmt.Errorf("expected to find one 'value' entry but found %v in position %v", valueResults.Len(), position) } return keyResults.Front().Value.(*CandidateNode).Node, valueResults.Front().Value.(*CandidateNode).Node, nil diff --git a/pkg/yqlib/operator_traverse_path.go b/pkg/yqlib/operator_traverse_path.go index 2b40282f..ca7141aa 100644 --- a/pkg/yqlib/operator_traverse_path.go +++ b/pkg/yqlib/operator_traverse_path.go @@ -3,7 +3,6 @@ package yqlib import ( "container/list" "fmt" - "strconv" "github.com/elliotchance/orderedmap" yaml "gopkg.in/yaml.v3" @@ -57,7 +56,7 @@ func traverse(context Context, matchingNode *CandidateNode, operation *Operation switch value.Kind { case yaml.MappingNode: log.Debug("its a map with %v entries", len(value.Content)/2) - return traverseMap(context, matchingNode, operation.StringValue, operation.Preferences.(traversePreferences), false) + return traverseMap(context, matchingNode, createStringScalarNode(operation.StringValue), operation.Preferences.(traversePreferences), false) case yaml.SequenceNode: log.Debug("its a sequence of %v things!", len(value.Content)) @@ -131,11 +130,8 @@ func traverseArrayIndices(context Context, matchingNode *CandidateNode, indicesT node.Tag = "" node.Kind = yaml.SequenceNode //check that the indices are numeric, if not, then we should create an object - if len(indicesToTraverse) != 0 { - _, err := strconv.ParseInt(indicesToTraverse[0].Value, 10, 64) - if err != nil { - node.Kind = yaml.MappingNode - } + if len(indicesToTraverse) != 0 && indicesToTraverse[0].Tag != "!!int" { + node.Kind = yaml.MappingNode } } @@ -155,14 +151,14 @@ func traverseArrayIndices(context Context, matchingNode *CandidateNode, indicesT func traverseMapWithIndices(context Context, candidate *CandidateNode, indices []*yaml.Node, prefs traversePreferences) (*list.List, error) { if len(indices) == 0 { - return traverseMap(context, candidate, "", prefs, true) + return traverseMap(context, candidate, createStringScalarNode(""), prefs, true) } var matchingNodeMap = list.New() for _, indexNode := range indices { log.Debug("traverseMapWithIndices: %v", indexNode.Value) - newNodes, err := traverseMap(context, candidate, indexNode.Value, prefs, false) + newNodes, err := traverseMap(context, candidate, indexNode, prefs, false) if err != nil { return nil, err } @@ -224,9 +220,9 @@ func keyMatches(key *yaml.Node, wantedKey string) bool { return matchKey(key.Value, wantedKey) } -func traverseMap(context Context, matchingNode *CandidateNode, key string, prefs traversePreferences, splat bool) (*list.List, error) { +func traverseMap(context Context, matchingNode *CandidateNode, keyNode *yaml.Node, prefs traversePreferences, splat bool) (*list.List, error) { var newMatches = orderedmap.NewOrderedMap() - err := doTraverseMap(newMatches, matchingNode, key, prefs, splat) + err := doTraverseMap(newMatches, matchingNode, keyNode.Value, prefs, splat) if err != nil { return nil, err @@ -235,7 +231,7 @@ func traverseMap(context Context, matchingNode *CandidateNode, key string, prefs if !prefs.DontAutoCreate && !context.DontAutoCreate && newMatches.Len() == 0 { //no matches, create one automagically valueNode := &yaml.Node{Tag: "!!null", Kind: yaml.ScalarNode, Value: "null"} - keyNode := &yaml.Node{Kind: yaml.ScalarNode, Value: key} + node := matchingNode.Node if len(node.Content) == 0 { diff --git a/pkg/yqlib/operator_traverse_path_test.go b/pkg/yqlib/operator_traverse_path_test.go index 464cbba5..56b3cc9c 100644 --- a/pkg/yqlib/operator_traverse_path_test.go +++ b/pkg/yqlib/operator_traverse_path_test.go @@ -44,6 +44,13 @@ var traversePathOperatorScenarios = []expressionScenario{ "D0, P[0 0], (!!int)::1\n", }, }, + { + skipDoc: true, + expression: `.cat["12"] = "things"`, + expected: []string{ + "D0, P[], ()::cat:\n \"12\": things\n", + }, + }, { skipDoc: true, document: `blah: {}`,