blob: 93b09289f82fbe117d4171227ee817d8f31d6078 [file] [log] [blame]
Joe Tsai879b18d2018-08-03 17:22:24 -07001// Copyright 2018 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package json
6
7import (
8 "strings"
9
Joe Tsai01ab2962018-09-21 17:44:00 -070010 "github.com/golang/protobuf/v2/internal/errors"
Joe Tsai879b18d2018-08-03 17:22:24 -070011)
12
13// Marshal serializes v as the JSON format.
14//
15// If indent is a non-empty string, it causes every entry for an Array or Object
16// to be preceded by the indent and trailed by a newline.
17func Marshal(v Value, indent string) ([]byte, error) {
18 p := encoder{}
19 if len(indent) > 0 {
20 if strings.Trim(indent, " \t") != "" {
21 return nil, errors.New("indent may only be composed of space and tab characters")
22 }
23 p.indent = indent
24 p.newline = "\n"
25 }
26 err := p.marshalValue(v)
27 if !p.nerr.Merge(err) {
28 return nil, err
29 }
30 return p.out, p.nerr.E
31}
32
33type encoder struct {
34 nerr errors.NonFatal
35 out []byte
36
37 indent string
38 indents []byte
39 newline string // set to "\n" if len(indent) > 0
40}
41
42func (p *encoder) marshalValue(v Value) error {
43 switch v.Type() {
44 case Null:
45 p.out = append(p.out, "null"...)
46 return nil
47 case Bool:
48 if v.Bool() {
49 p.out = append(p.out, "true"...)
50 } else {
51 p.out = append(p.out, "false"...)
52 }
53 return nil
54 case Number:
55 return p.marshalNumber(v)
56 case String:
57 return p.marshalString(v)
58 case Array:
59 return p.marshalArray(v)
60 case Object:
61 return p.marshalObject(v)
62 default:
63 return errors.New("invalid type %v to encode value", v.Type())
64 }
65}
66
67func (p *encoder) marshalArray(v Value) error {
68 if v.Type() != Array {
69 return errors.New("invalid type %v, expected array", v.Type())
70 }
71 elems := v.Array()
72 p.out = append(p.out, '[')
73 p.indents = append(p.indents, p.indent...)
74 if len(elems) > 0 {
75 p.out = append(p.out, p.newline...)
76 }
77 for i, elem := range elems {
78 p.out = append(p.out, p.indents...)
79 if err := p.marshalValue(elem); !p.nerr.Merge(err) {
80 return err
81 }
82 if i < len(elems)-1 {
83 p.out = append(p.out, ',')
84 }
85 p.out = append(p.out, p.newline...)
86 }
87 p.indents = p.indents[:len(p.indents)-len(p.indent)]
88 if len(elems) > 0 {
89 p.out = append(p.out, p.indents...)
90 }
91 p.out = append(p.out, ']')
92 return nil
93}
94
95func (p *encoder) marshalObject(v Value) error {
96 if v.Type() != Object {
97 return errors.New("invalid type %v, expected object", v.Type())
98 }
99 items := v.Object()
100 p.out = append(p.out, '{')
101 p.indents = append(p.indents, p.indent...)
102 if len(items) > 0 {
103 p.out = append(p.out, p.newline...)
104 }
105 for i, item := range items {
106 p.out = append(p.out, p.indents...)
107 if err := p.marshalString(item[0]); !p.nerr.Merge(err) {
108 return err
109 }
110 p.out = append(p.out, ':')
111 if len(p.indent) > 0 {
112 p.out = append(p.out, ' ')
113 }
114 if err := p.marshalValue(item[1]); !p.nerr.Merge(err) {
115 return err
116 }
117 if i < len(items)-1 {
118 p.out = append(p.out, ',')
119 }
120 p.out = append(p.out, p.newline...)
121 }
122 p.indents = p.indents[:len(p.indents)-len(p.indent)]
123 if len(items) > 0 {
124 p.out = append(p.out, p.indents...)
125 }
126 p.out = append(p.out, '}')
127 return nil
128}