1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
|
package json
import (
"bytes"
"embed"
stdjson "encoding/json"
"errors"
"io"
"github.com/wader/fq/format"
"github.com/wader/fq/internal/colorjson"
"github.com/wader/fq/pkg/bitio"
"github.com/wader/fq/pkg/decode"
"github.com/wader/fq/pkg/interp"
"github.com/wader/fq/pkg/scalar"
"github.com/wader/gojq"
)
//go:embed json.jq
var jsonFS embed.FS
func init() {
interp.RegisterFormat(
format.JSON,
&decode.Format{
Description: "JavaScript Object Notation",
ProbeOrder: format.ProbeOrderTextJSON,
Groups: []*decode.Group{format.Probe},
DecodeFn: decodeJSON,
Functions: []string{"_todisplay"},
})
interp.RegisterFS(jsonFS)
interp.RegisterFunc1("_to_json", toJSON)
}
func decodeJSONEx(d *decode.D, lines bool) any {
var vs []any
// keep in sync with gojq fromJSON
jd := stdjson.NewDecoder(bitio.NewIOReader(d.RawLen(d.Len())))
jd.UseNumber()
foundEOF := false
for {
var v any
if err := jd.Decode(&v); err != nil {
if errors.Is(err, io.EOF) {
foundEOF = true
if lines {
break
} else if len(vs) == 1 {
break
}
} else if lines {
d.Fatalf(err.Error())
}
break
}
vs = append(vs, v)
}
if !lines && (len(vs) != 1 || !foundEOF) {
d.Fatalf("trialing data after top-level value")
}
var s scalar.Any
if lines {
if len(vs) == 0 {
d.Fatalf("not lines found")
}
s.Actual = gojq.NormalizeNumbers(vs)
} else {
s.Actual = gojq.NormalizeNumbers(vs[0])
}
d.Value.V = &s
d.Value.Range.Len = d.Len()
return nil
}
func decodeJSON(d *decode.D) any {
return decodeJSONEx(d, false)
}
type ToJSONOpts struct {
Indent int
}
// TODO: share with interp code
func makeEncoder(opts ToJSONOpts) *colorjson.Encoder {
return colorjson.NewEncoder(colorjson.Options{
Color: false,
Tab: false,
Indent: opts.Indent,
ValueFn: func(v any) (any, error) {
switch v := v.(type) {
case gojq.JQValue:
return v.JQValueToGoJQ(), nil
default:
return v, nil
}
},
Colors: colorjson.Colors{},
})
}
func toJSON(_ *interp.Interp, c any, opts ToJSONOpts) any {
cj := makeEncoder(opts)
bb := &bytes.Buffer{}
if err := cj.Marshal(c, bb); err != nil {
return err
}
return bb.String()
}
|