From 9c9764f9a939e0ee3337190d25acfc3e4c5c5cc7 Mon Sep 17 00:00:00 2001 From: evnp Date: Sat, 16 Jan 2021 22:09:22 -0800 Subject: [PATCH] Don't escape HTML chars when converting to json json.Encoder and json.Marshal implicitly use HTMLEscape to convert >, <, &, with \u003c, \u003e, \u0026. This behavior carries over to yq, where chars will be escaped when outputting json but not when outputting yaml. This changeset disables this behavior via encoder.SetEscapeHTML(false). Unfortunately there is no equivalent option for json.Marshal, so its single usage has been replaced with an encoder (with escaping disabled). --- pkg/yqlib/encoder.go | 12 ++++++--- pkg/yqlib/encoder_test.go | 51 +++++++++++++++++++++++---------------- 2 files changed, 39 insertions(+), 24 deletions(-) diff --git a/pkg/yqlib/encoder.go b/pkg/yqlib/encoder.go index 11bad1e8..3c803140 100644 --- a/pkg/yqlib/encoder.go +++ b/pkg/yqlib/encoder.go @@ -76,6 +76,8 @@ func mapKeysToStrings(node *yaml.Node) { func NewJsonEncoder(destination io.Writer, indent int) Encoder { var encoder = json.NewEncoder(destination) + encoder.SetEscapeHTML(false) // do not escape html chars e.g. &, <, > + var indentString = "" for index := 0; index < indent; index++ { @@ -153,11 +155,15 @@ func (o *orderedMap) UnmarshalJSON(data []byte) error { } func (o orderedMap) MarshalJSON() ([]byte, error) { - if o.kv == nil { - return json.Marshal(o.altVal) - } buf := new(bytes.Buffer) enc := json.NewEncoder(buf) + enc.SetEscapeHTML(false) // do not escape html chars e.g. &, <, > + if o.kv == nil { + if err := enc.Encode(o.altVal); err != nil { + return nil, err + } + return buf.Bytes(), nil + } buf.WriteByte('{') for idx, el := range o.kv { if err := enc.Encode(el.K); err != nil { diff --git a/pkg/yqlib/encoder_test.go b/pkg/yqlib/encoder_test.go index d7211734..333749d9 100644 --- a/pkg/yqlib/encoder_test.go +++ b/pkg/yqlib/encoder_test.go @@ -9,29 +9,11 @@ import ( "github.com/mikefarah/yq/v4/test" ) -var sampleYaml = `zabbix: winner -apple: great -banana: -- {cobra: kai, angus: bob} -` - -var expectedJson = `{ - "zabbix": "winner", - "apple": "great", - "banana": [ - { - "cobra": "kai", - "angus": "bob" - } - ] -} -` - -func TestJsonEncoderPreservesObjectOrder(t *testing.T) { +func yamlToJson(sampleYaml string, indent int) string { var output bytes.Buffer writer := bufio.NewWriter(&output) - var jsonEncoder = NewJsonEncoder(writer, 2) + var jsonEncoder = NewJsonEncoder(writer, indent) inputs, err := readDocuments(strings.NewReader(sampleYaml), "sample.yml", 0) if err != nil { panic(err) @@ -42,6 +24,33 @@ func TestJsonEncoderPreservesObjectOrder(t *testing.T) { panic(err) } writer.Flush() - test.AssertResult(t, expectedJson, output.String()) + return strings.TrimSuffix(output.String(), "\n") +} + +func TestJsonEncoderPreservesObjectOrder(t *testing.T) { + var sampleYaml = `zabbix: winner +apple: great +banana: +- {cobra: kai, angus: bob} +` + var expectedJson = `{ + "zabbix": "winner", + "apple": "great", + "banana": [ + { + "cobra": "kai", + "angus": "bob" + } + ] +}` + var actualJson = yamlToJson(sampleYaml, 2) + test.AssertResult(t, expectedJson, actualJson) +} + +func TestJsonEncoderDoesNotEscapeHTMLChars(t *testing.T) { + var sampleYaml = `build: "( ./lint && ./format && ./compile ) < src.code"` + var expectedJson = `{"build":"( ./lint && ./format && ./compile ) < src.code"}` + var actualJson = yamlToJson(sampleYaml, 0) + test.AssertResult(t, expectedJson, actualJson) }