Source file src/pkg/encoding/json/indent.go
1 // Copyright 2010 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
5 package json
6
7 import "bytes"
8
9 // Compact appends to dst the JSON-encoded src with
10 // insignificant space characters elided.
11 func Compact(dst *bytes.Buffer, src []byte) error {
12 return compact(dst, src, false)
13 }
14
15 func compact(dst *bytes.Buffer, src []byte, escape bool) error {
16 origLen := dst.Len()
17 var scan scanner
18 scan.reset()
19 start := 0
20 for i, c := range src {
21 if escape && (c == '<' || c == '>' || c == '&') {
22 if start < i {
23 dst.Write(src[start:i])
24 }
25 dst.WriteString(`\u00`)
26 dst.WriteByte(hex[c>>4])
27 dst.WriteByte(hex[c&0xF])
28 start = i + 1
29 }
30 v := scan.step(&scan, int(c))
31 if v >= scanSkipSpace {
32 if v == scanError {
33 break
34 }
35 if start < i {
36 dst.Write(src[start:i])
37 }
38 start = i + 1
39 }
40 }
41 if scan.eof() == scanError {
42 dst.Truncate(origLen)
43 return scan.err
44 }
45 if start < len(src) {
46 dst.Write(src[start:])
47 }
48 return nil
49 }
50
51 func newline(dst *bytes.Buffer, prefix, indent string, depth int) {
52 dst.WriteByte('\n')
53 dst.WriteString(prefix)
54 for i := 0; i < depth; i++ {
55 dst.WriteString(indent)
56 }
57 }
58
59 // Indent appends to dst an indented form of the JSON-encoded src.
60 // Each element in a JSON object or array begins on a new,
61 // indented line beginning with prefix followed by one or more
62 // copies of indent according to the indentation nesting.
63 // The data appended to dst has no trailing newline, to make it easier
64 // to embed inside other formatted JSON data.
65 func Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error {
66 origLen := dst.Len()
67 var scan scanner
68 scan.reset()
69 needIndent := false
70 depth := 0
71 for _, c := range src {
72 scan.bytes++
73 v := scan.step(&scan, int(c))
74 if v == scanSkipSpace {
75 continue
76 }
77 if v == scanError {
78 break
79 }
80 if needIndent && v != scanEndObject && v != scanEndArray {
81 needIndent = false
82 depth++
83 newline(dst, prefix, indent, depth)
84 }
85
86 // Emit semantically uninteresting bytes
87 // (in particular, punctuation in strings) unmodified.
88 if v == scanContinue {
89 dst.WriteByte(c)
90 continue
91 }
92
93 // Add spacing around real punctuation.
94 switch c {
95 case '{', '[':
96 // delay indent so that empty object and array are formatted as {} and [].
97 needIndent = true
98 dst.WriteByte(c)
99
100 case ',':
101 dst.WriteByte(c)
102 newline(dst, prefix, indent, depth)
103
104 case ':':
105 dst.WriteByte(c)
106 dst.WriteByte(' ')
107
108 case '}', ']':
109 if needIndent {
110 // suppress indent in empty object/array
111 needIndent = false
112 } else {
113 depth--
114 newline(dst, prefix, indent, depth)
115 }
116 dst.WriteByte(c)
117
118 default:
119 dst.WriteByte(c)
120 }
121 }
122 if scan.eof() == scanError {
123 dst.Truncate(origLen)
124 return scan.err
125 }
126 return nil
127 }