123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137 |
- // Copyright 2010 The Go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- package json
- import "bytes"
- // Compact appends to dst the JSON-encoded src with
- // insignificant space characters elided.
- func Compact(dst *bytes.Buffer, src []byte) error {
- return compact(dst, src, false)
- }
- func compact(dst *bytes.Buffer, src []byte, escape bool) error {
- origLen := dst.Len()
- var scan Scanner
- scan.Reset()
- start := 0
- for i, c := range src {
- if escape && (c == '<' || c == '>' || c == '&') {
- if start < i {
- dst.Write(src[start:i])
- }
- dst.WriteString(`\u00`)
- dst.WriteByte(hex[c>>4])
- dst.WriteByte(hex[c&0xF])
- start = i + 1
- }
- // Convert U+2028 and U+2029 (E2 80 A8 and E2 80 A9).
- if c == 0xE2 && i+2 < len(src) && src[i+1] == 0x80 && src[i+2]&^1 == 0xA8 {
- if start < i {
- dst.Write(src[start:i])
- }
- dst.WriteString(`\u202`)
- dst.WriteByte(hex[src[i+2]&0xF])
- start = i + 3
- }
- v := scan.Step(&scan, int(c))
- if v >= ScanSkipSpace {
- if v == ScanError {
- break
- }
- if start < i {
- dst.Write(src[start:i])
- }
- start = i + 1
- }
- }
- if scan.EOF() == ScanError {
- dst.Truncate(origLen)
- return scan.Err
- }
- if start < len(src) {
- dst.Write(src[start:])
- }
- return nil
- }
- func newline(dst *bytes.Buffer, prefix, indent string, depth int) {
- dst.WriteByte('\n')
- dst.WriteString(prefix)
- for i := 0; i < depth; i++ {
- dst.WriteString(indent)
- }
- }
- // Indent appends to dst an indented form of the JSON-encoded src.
- // Each element in a JSON object or array begins on a new,
- // indented line beginning with prefix followed by one or more
- // copies of indent according to the indentation nesting.
- // The data appended to dst does not begin with the prefix nor
- // any indentation, and has no trailing newline, to make it
- // easier to embed inside other formatted JSON data.
- func Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error {
- origLen := dst.Len()
- var scan Scanner
- scan.Reset()
- needIndent := false
- depth := 0
- for _, c := range src {
- scan.bytes++
- v := scan.Step(&scan, int(c))
- if v == ScanSkipSpace {
- continue
- }
- if v == ScanError {
- break
- }
- if needIndent && v != ScanEndObject && v != ScanEndArray {
- needIndent = false
- depth++
- newline(dst, prefix, indent, depth)
- }
- // Emit semantically uninteresting bytes
- // (in particular, punctuation in strings) unmodified.
- if v == ScanContinue {
- dst.WriteByte(c)
- continue
- }
- // Add spacing around real punctuation.
- switch c {
- case '{', '[':
- // delay indent so that empty object and array are formatted as {} and [].
- needIndent = true
- dst.WriteByte(c)
- case ',':
- dst.WriteByte(c)
- newline(dst, prefix, indent, depth)
- case ':':
- dst.WriteByte(c)
- dst.WriteByte(' ')
- case '}', ']':
- if needIndent {
- // suppress indent in empty object/array
- needIndent = false
- } else {
- depth--
- newline(dst, prefix, indent, depth)
- }
- dst.WriteByte(c)
- default:
- dst.WriteByte(c)
- }
- }
- if scan.EOF() == ScanError {
- dst.Truncate(origLen)
- return scan.Err
- }
- return nil
- }
|