File: json_encoder.go

package info (click to toggle)
dasel 2.8.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 5,844 kB
  • sloc: sh: 53; python: 21; makefile: 21; xml: 20
file content (92 lines) | stat: -rw-r--r-- 2,323 bytes parent folder | download
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
}