Skip to content

Commit

Permalink
implement and export Marshal for jq-flavored encoding
Browse files Browse the repository at this point in the history
  • Loading branch information
itchyny committed Dec 24, 2020
1 parent 01022a4 commit 16ae8ff
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 5 deletions.
26 changes: 21 additions & 5 deletions encoder.go
Original file line number Diff line number Diff line change
@@ -1,23 +1,39 @@
package gojq

import (
"bytes"
"fmt"
"math"
"math/big"
"sort"
"strconv"
"strings"
"unicode/utf8"
)

// Marshal returns the jq-flavored JSON encoding of v.
//
// This method only accepts limited types (nil, bool, int, float64, *big.Int,
// string, []interface{} and map[string]interface{}) because these are the
// possible types a gojq iterator can emit. This method marshals NaN to null,
// truncates infinities to (+|-) math.MaxFloat64 and does not escape '<' and
// '>' for embedding in HTML. These behaviors are based on the marshaler of jq
// command and different from the standard library method json.Marshal.
func Marshal(v interface{}) ([]byte, error) {
return jsonMarshalBytes(v), nil
}

func jsonMarshal(v interface{}) string {
var s strings.Builder
(&encoder{w: &s}).encode(v)
return s.String()
return string(jsonMarshalBytes(v))
}

func jsonMarshalBytes(v interface{}) []byte {
var b bytes.Buffer
(&encoder{w: &b}).encode(v)
return b.Bytes()
}

type encoder struct {
w *strings.Builder
w *bytes.Buffer
buf [64]byte
}

Expand Down
58 changes: 58 additions & 0 deletions encoder_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package gojq

import (
"math"
"math/big"
"testing"
)

func TestMarshal(t *testing.T) {
testCases := []struct {
name string
value interface{}
expected string
}{
{
name: "nil",
value: nil,
expected: "null",
},
{
name: "booleans",
value: []interface{}{false, true},
expected: "[false,true]",
},
{
name: "numbers",
value: []interface{}{42, 3.14, 1e-6, 1e-7, -1e-9, 1e-10, math.NaN(), math.Inf(1), math.Inf(-1),
new(big.Int).SetBytes([]byte("\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"))},
expected: "[42,3.14,0.000001,1e-7,-1e-9,1e-10,null,1.7976931348623157e+308,-1.7976931348623157e+308,340282366920938463463374607431768211455]",
},
{
name: "strings",
value: []interface{}{"", "abcde", "foo\x00\x1f\r\t\n\f\b<=>!\"#$%'& \\\x7fbar"},
expected: `["","abcde","foo\u0000\u001f\r\t\n\u000c\u0008<=>!\"#$%'& \\\u007fbar"]`,
},
{
name: "arrays",
value: []interface{}{1, []interface{}{2, []interface{}{3, []interface{}{map[string]interface{}{}}}}},
expected: `[1,[2,[3,[{}]]]]`,
},
{
name: "objects",
value: map[string]interface{}{"x": []interface{}{100}, "y": map[string]interface{}{"z": 42}},
expected: `{"x":[100],"y":{"z":42}}`,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
got, err := Marshal(tc.value)
if err != nil {
t.Fatal(err)
}
if string(got) != tc.expected {
t.Errorf("expected: %s, got: %s", tc.expected, string(got))
}
})
}
}

0 comments on commit 16ae8ff

Please sign in to comment.