Skip to content

Commit

Permalink
Support JSON marshalling of yaml sequence and scalar nodes (#220)
Browse files Browse the repository at this point in the history
  • Loading branch information
bvwells authored Oct 14, 2020
1 parent 52a6193 commit 7fb1841
Show file tree
Hide file tree
Showing 3 changed files with 207 additions and 3 deletions.
3 changes: 1 addition & 2 deletions jsonwriter/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# jsonwriter

This directory contains code for writing yaml.MapSlice structures as JSON
files.
This directory contains code for writing yaml.Node structures as JSON.
8 changes: 7 additions & 1 deletion jsonwriter/writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ func (w *writer) writeSequence(node *yaml.Node, indent string) {
w.writeString("]")
}

// Marshal writes a yaml.MapSlice as JSON
// Marshal writes a yaml.Node as JSON
func Marshal(in *yaml.Node) (out []byte, err error) {
var w writer

Expand All @@ -134,6 +134,12 @@ func Marshal(in *yaml.Node) (out []byte, err error) {
case yaml.MappingNode:
w.writeMap(in, "")
w.writeString("\n")
case yaml.SequenceNode:
w.writeSequence(in, "")
w.writeString("\n")
case yaml.ScalarNode:
w.writeScalar(in, "")
w.writeString("\n")
default:
return nil, errors.New("invalid type passed to Marshal")
}
Expand Down
199 changes: 199 additions & 0 deletions jsonwriter/writer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
// Copyright 2017 Google LLC. All Rights Reserved.
//
// 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.

package jsonwriter_test

import (
"testing"

"github.com/googleapis/gnostic/compiler"
"github.com/googleapis/gnostic/jsonwriter"

"gopkg.in/yaml.v3"
)

type MarshalTestCase struct {
Name string
Node *yaml.Node
Expected string
Err bool
}

func TestMarshal(t *testing.T) {
t.Parallel()
tests := []*MarshalTestCase{
scalarNodeTestCase(),
scalarBoolTestCase(),
scalarFloatTestCase(),
scalarIntTestCase(),
sequenceStringArrayTestCase(),
sequenceBoolArrayTestCase(),
sequenceFloatArrayTestCase(),
sequenceIntArrayTestCase(),
sequenceSequenceStringArrayTestCase(),
sequenceMappingNodeTestCase(),
mappingNodeTestCase(),
documentNodeTestCase(),
aliasNodeTestCase(),
}

for _, test := range tests {
t.Run(test.Name, func(s *testing.T) {
b, err := jsonwriter.Marshal(test.Node)
if err != nil && !test.Err {
s.Errorf("expected %v to be nil", err)
}
if err == nil && test.Err {
s.Error("expected error")
}
if string(b) != test.Expected {
s.Errorf("expected %v to equal %v", string(b), test.Expected)
}
})
}
}

func scalarNodeTestCase() *MarshalTestCase {
return &MarshalTestCase{
Name: "scalar string",
Node: compiler.NewScalarNodeForString("expected"),
Expected: "\"expected\"\n",
}
}

func scalarBoolTestCase() *MarshalTestCase {
return &MarshalTestCase{
Name: "scalar bool",
Node: compiler.NewScalarNodeForBool(true),
Expected: "true\n",
}
}

func scalarFloatTestCase() *MarshalTestCase {
return &MarshalTestCase{
Name: "scalar float",
Node: compiler.NewScalarNodeForFloat(42.1),
Expected: "42.1\n",
}
}

func scalarIntTestCase() *MarshalTestCase {
return &MarshalTestCase{
Name: "scalar int",
Node: compiler.NewScalarNodeForInt(42),
Expected: "42\n",
}
}

func sequenceStringArrayTestCase() *MarshalTestCase {
return &MarshalTestCase{
Name: "sequence string array",
Node: compiler.NewSequenceNodeForStringArray([]string{"a", "b", "c"}),
Expected: "[\n \"a\",\n \"b\",\n \"c\"\n]\n",
}
}

func sequenceBoolArrayTestCase() *MarshalTestCase {
node := compiler.NewSequenceNode()
for _, b := range []bool{true, false, true} {
node.Content = append(node.Content, compiler.NewScalarNodeForBool(b))
}
return &MarshalTestCase{
Name: "sequence bool array",
Node: node,
Expected: "[\n true,\n false,\n true\n]\n",
}
}

func sequenceFloatArrayTestCase() *MarshalTestCase {
node := compiler.NewSequenceNode()
for _, f := range []float64{1.1, 2.2, 3.3} {
node.Content = append(node.Content, compiler.NewScalarNodeForFloat(f))
}
return &MarshalTestCase{
Name: "sequence float array",
Node: node,
Expected: "[\n 1.1,\n 2.2,\n 3.3\n]\n",
}
}

func sequenceIntArrayTestCase() *MarshalTestCase {
node := compiler.NewSequenceNode()
for _, i := range []int64{1, 2, 3} {
node.Content = append(node.Content, compiler.NewScalarNodeForInt(i))
}
return &MarshalTestCase{
Name: "sequence int array",
Node: node,
Expected: "[\n 1,\n 2,\n 3\n]\n",
}
}

func sequenceSequenceStringArrayTestCase() *MarshalTestCase {
node := compiler.NewSequenceNode()
node.Content = append(node.Content, compiler.NewSequenceNodeForStringArray([]string{"a", "b", "c"}))
node.Content = append(node.Content, compiler.NewSequenceNodeForStringArray([]string{"e", "f"}))
return &MarshalTestCase{
Name: "sequence sequence string array",
Node: node,
Expected: "[\n [\n \"a\",\n \"b\",\n \"c\"\n ],\n [\n \"e\",\n \"f\"\n ]\n]\n",
}
}

func sequenceMappingNodeTestCase() *MarshalTestCase {
m := compiler.NewMappingNode()
m.Content = append(m.Content, compiler.NewScalarNodeForString("required"))
m.Content = append(m.Content, compiler.NewSequenceNodeForStringArray([]string{"a", "b", "c"}))
node := compiler.NewSequenceNode()
node.Content = append(node.Content, m)
return &MarshalTestCase{
Name: "sequence mapping node array",
Node: node,
Expected: "[\n {\n \"required\": [\n \"a\",\n \"b\",\n \"c\"\n ]\n }\n]\n",
}
}

func mappingNodeTestCase() *MarshalTestCase {
node := compiler.NewMappingNode()
node.Content = append(node.Content, compiler.NewScalarNodeForString("required"))
node.Content = append(node.Content, compiler.NewSequenceNodeForStringArray([]string{"a", "b", "c"}))
return &MarshalTestCase{
Name: "Mapping node",
Node: node,
Expected: "{\n \"required\": [\n \"a\",\n \"b\",\n \"c\"\n ]\n}\n",
}
}

func documentNodeTestCase() *MarshalTestCase {
m := compiler.NewMappingNode()
m.Content = append(m.Content, compiler.NewScalarNodeForString("version"))
m.Content = append(m.Content, compiler.NewScalarNodeForString("1.0.0"))
node := &yaml.Node{
Kind: yaml.DocumentNode,
Content: []*yaml.Node{m},
}
return &MarshalTestCase{
Name: "Document node",
Node: node,
Expected: "{\n \"version\": \"1.0.0\"\n}\n",
}
}

func aliasNodeTestCase() *MarshalTestCase {
return &MarshalTestCase{
Name: "unsupported alias node",
Node: &yaml.Node{Kind: yaml.AliasNode},
Err: true,
}
}

0 comments on commit 7fb1841

Please sign in to comment.