...

Source file src/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			// Convert U+2028 and U+2029 (E2 80 A8 and E2 80 A9).
    31			if c == 0xE2 && i+2 < len(src) && src[i+1] == 0x80 && src[i+2]&^1 == 0xA8 {
    32				if start < i {
    33					dst.Write(src[start:i])
    34				}
    35				dst.WriteString(`\u202`)
    36				dst.WriteByte(hex[src[i+2]&0xF])
    37				start = i + 3
    38			}
    39			v := scan.step(&scan, c)
    40			if v >= scanSkipSpace {
    41				if v == scanError {
    42					break
    43				}
    44				if start < i {
    45					dst.Write(src[start:i])
    46				}
    47				start = i + 1
    48			}
    49		}
    50		if scan.eof() == scanError {
    51			dst.Truncate(origLen)
    52			return scan.err
    53		}
    54		if start < len(src) {
    55			dst.Write(src[start:])
    56		}
    57		return nil
    58	}
    59	
    60	func newline(dst *bytes.Buffer, prefix, indent string, depth int) {
    61		dst.WriteByte('\n')
    62		dst.WriteString(prefix)
    63		for i := 0; i < depth; i++ {
    64			dst.WriteString(indent)
    65		}
    66	}
    67	
    68	// Indent appends to dst an indented form of the JSON-encoded src.
    69	// Each element in a JSON object or array begins on a new,
    70	// indented line beginning with prefix followed by one or more
    71	// copies of indent according to the indentation nesting.
    72	// The data appended to dst does not begin with the prefix nor
    73	// any indentation, to make it easier to embed inside other formatted JSON data.
    74	// Although leading space characters (space, tab, carriage return, newline)
    75	// at the beginning of src are dropped, trailing space characters
    76	// at the end of src are preserved and copied to dst.
    77	// For example, if src has no trailing spaces, neither will dst;
    78	// if src ends in a trailing newline, so will dst.
    79	func Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error {
    80		origLen := dst.Len()
    81		var scan scanner
    82		scan.reset()
    83		needIndent := false
    84		depth := 0
    85		for _, c := range src {
    86			scan.bytes++
    87			v := scan.step(&scan, c)
    88			if v == scanSkipSpace {
    89				continue
    90			}
    91			if v == scanError {
    92				break
    93			}
    94			if needIndent && v != scanEndObject && v != scanEndArray {
    95				needIndent = false
    96				depth++
    97				newline(dst, prefix, indent, depth)
    98			}
    99	
   100			// Emit semantically uninteresting bytes
   101			// (in particular, punctuation in strings) unmodified.
   102			if v == scanContinue {
   103				dst.WriteByte(c)
   104				continue
   105			}
   106	
   107			// Add spacing around real punctuation.
   108			switch c {
   109			case '{', '[':
   110				// delay indent so that empty object and array are formatted as {} and [].
   111				needIndent = true
   112				dst.WriteByte(c)
   113	
   114			case ',':
   115				dst.WriteByte(c)
   116				newline(dst, prefix, indent, depth)
   117	
   118			case ':':
   119				dst.WriteByte(c)
   120				dst.WriteByte(' ')
   121	
   122			case '}', ']':
   123				if needIndent {
   124					// suppress indent in empty object/array
   125					needIndent = false
   126				} else {
   127					depth--
   128					newline(dst, prefix, indent, depth)
   129				}
   130				dst.WriteByte(c)
   131	
   132			default:
   133				dst.WriteByte(c)
   134			}
   135		}
   136		if scan.eof() == scanError {
   137			dst.Truncate(origLen)
   138			return scan.err
   139		}
   140		return nil
   141	}
   142	

View as plain text