Go Home Page
The Go Programming Language

Source file src/pkg/json/indent.go

// 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"
    "os"
)

// Compact appends to dst the JSON-encoded src with
// insignificant space characters elided.
func Compact(dst *bytes.Buffer, src []byte) os.Error {
    origLen := dst.Len()
    var scan scanner
    scan.reset()
    start := 0
    for i, c := range src {
        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 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) os.Error {
    origLen := dst.Len()
    var scan scanner
    scan.reset()
    needIndent := false
    depth := 0
    for _, c := range src {
        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
}