databricks-cli/libs/dyn/jsonsaver/marshal.go

90 lines
2.4 KiB
Go

package jsonsaver
import (
"bytes"
"fmt"
"github.com/databricks/cli/libs/dyn"
)
// Marshal is a version of [json.Marshal] for [dyn.Value].
//
// Objects in the output retain the order of keys as they appear in the underlying [dyn.Value].
// The output does not escape HTML characters in strings.
func Marshal(v dyn.Value) ([]byte, error) {
return marshalNoEscape(wrap{v})
}
// MarshalIndent is a version of [json.MarshalIndent] for [dyn.Value].
//
// Objects in the output retain the order of keys as they appear in the underlying [dyn.Value].
// The output does not escape HTML characters in strings.
func MarshalIndent(v dyn.Value, prefix, indent string) ([]byte, error) {
return marshalIndentNoEscape(wrap{v}, prefix, indent)
}
// Wrapper type for [dyn.Value] to expose the [json.Marshaler] interface.
type wrap struct {
v dyn.Value
}
// MarshalJSON implements the [json.Marshaler] interface for the [dyn.Value] wrapper type.
func (w wrap) MarshalJSON() ([]byte, error) {
var buf bytes.Buffer
if err := marshalValue(&buf, w.v); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// marshalValue recursively writes JSON for a [dyn.Value] to the buffer.
func marshalValue(buf *bytes.Buffer, v dyn.Value) error {
switch v.Kind() {
case dyn.KindString, dyn.KindBool, dyn.KindInt, dyn.KindFloat, dyn.KindTime, dyn.KindNil:
out, err := marshalNoEscape(v.AsAny())
if err != nil {
return err
}
// The encoder writes a trailing newline, so we need to remove it
// to avoid adding extra newlines when embedding this JSON.
out = out[:len(out)-1]
buf.Write(out)
case dyn.KindMap:
buf.WriteByte('{')
for i, pair := range v.MustMap().Pairs() {
if i > 0 {
buf.WriteByte(',')
}
// Require keys to be strings.
if pair.Key.Kind() != dyn.KindString {
return fmt.Errorf("map key must be a string, got %s", pair.Key.Kind())
}
// Marshal the key
if err := marshalValue(buf, pair.Key); err != nil {
return err
}
buf.WriteByte(':')
// Marshal the value
if err := marshalValue(buf, pair.Value); err != nil {
return err
}
}
buf.WriteByte('}')
case dyn.KindSequence:
buf.WriteByte('[')
for i, item := range v.MustSequence() {
if i > 0 {
buf.WriteByte(',')
}
if err := marshalValue(buf, item); err != nil {
return err
}
}
buf.WriteByte(']')
default:
return fmt.Errorf("unsupported kind: %d", v.Kind())
}
return nil
}