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
|
package dencoding
import (
"bytes"
"encoding/json"
"io"
)
// lastOptions contains the options that the last JSONEncoder was created with.
// Find a better way of passing this information into nested MarshalJSON calls.
var lastOptions []JSONEncoderOption
// JSONEncoder wraps a standard json encoder to implement custom ordering logic.
type JSONEncoder struct {
encoder *json.Encoder
}
// NewJSONEncoder returns a new dencoding JSONEncoder.
func NewJSONEncoder(w io.Writer, options ...JSONEncoderOption) *JSONEncoder {
jsonEncoder := json.NewEncoder(w)
encoder := &JSONEncoder{
encoder: jsonEncoder,
}
for _, o := range options {
o.ApplyEncoder(encoder)
}
lastOptions = options
return encoder
}
// Encode encodes the given value and writes the encodes bytes to the stream.
func (encoder *JSONEncoder) Encode(v any) error {
// We rely on Map.MarshalJSON to ensure ordering.
return encoder.encoder.Encode(v)
}
// Close cleans up the encoder.
func (encoder *JSONEncoder) Close() error {
return nil
}
// JSONEscapeHTML enables or disables html escaping when encoding JSON.
func JSONEscapeHTML(escape bool) JSONEncoderOption {
return jsonEncodeHTMLOption{escapeHTML: escape}
}
type jsonEncodeHTMLOption struct {
escapeHTML bool
}
func (option jsonEncodeHTMLOption) ApplyEncoder(encoder *JSONEncoder) {
encoder.encoder.SetEscapeHTML(option.escapeHTML)
}
// JSONEncodeIndent sets the indentation when encoding JSON.
func JSONEncodeIndent(prefix string, indent string) JSONEncoderOption {
return jsonEncodeIndent{prefix: prefix, indent: indent}
}
type jsonEncodeIndent struct {
prefix string
indent string
}
func (option jsonEncodeIndent) ApplyEncoder(encoder *JSONEncoder) {
encoder.encoder.SetIndent(option.prefix, option.indent)
}
// MarshalJSON JSON encodes the map and returns the bytes.
// This maintains ordering.
func (m *Map) MarshalJSON() ([]byte, error) {
buf := new(bytes.Buffer)
buf.Write([]byte(`{`))
encoder := NewJSONEncoder(buf, lastOptions...)
for i, key := range m.keys {
last := i == len(m.keys)-1
if err := encoder.Encode(key); err != nil {
return nil, err
}
buf.Write([]byte(`:`))
if err := encoder.Encode(m.data[key]); err != nil {
return nil, err
}
if !last {
buf.Write([]byte(`,`))
}
}
buf.Write([]byte(`}`))
return buf.Bytes(), nil
}
|