├── examples ├── simple.go └── indent.go ├── LICENSE ├── colorjson_test.go ├── README.md └── colorjson.go /examples/simple.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/TylerBrock/colorjson" 6 | "encoding/json" 7 | ) 8 | 9 | func main() { 10 | str := `{ 11 | "str": "foo", 12 | "num": 100, 13 | "bool": false, 14 | "null": null, 15 | "array": ["foo", "bar", "baz"], 16 | "obj": { "a": 1, "b": 2 } 17 | }` 18 | 19 | var obj map[string]interface{} 20 | json.Unmarshal([]byte(str), &obj) 21 | 22 | // Marshall the Colorized JSON 23 | s, _ := colorjson.Marshal(obj) 24 | fmt.Println(string(s)) 25 | } 26 | 27 | -------------------------------------------------------------------------------- /examples/indent.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/TylerBrock/colorjson" 6 | "encoding/json" 7 | ) 8 | 9 | func main() { 10 | str := `{ 11 | "str": "foo", 12 | "num": 100, 13 | "bool": false, 14 | "null": null, 15 | "array": ["foo", "bar", "baz"], 16 | "obj": { "a": 1, "b": 2 } 17 | }` 18 | 19 | var obj map[string]interface{} 20 | json.Unmarshal([]byte(str), &obj) 21 | 22 | // Make a custom formatter with indent set 23 | f := colorjson.NewFormatter() 24 | f.Indent = 4 25 | 26 | // Marshall the Colorized JSON 27 | s, _ := f.Marshal(obj) 28 | fmt.Println(string(s)) 29 | } 30 | 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Tyler Brock 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /colorjson_test.go: -------------------------------------------------------------------------------- 1 | package colorjson_test 2 | 3 | import "testing" 4 | import "github.com/TylerBrock/colorjson" 5 | import "github.com/hokaccha/go-prettyjson" 6 | 7 | func benchmarkMarshall(i int, b *testing.B) { 8 | simpleMap := make(map[string]interface{}) 9 | simpleMap["a"] = 1 10 | simpleMap["b"] = "bee" 11 | simpleMap["c"] = [3]float64{1, 2, 3} 12 | simpleMap["d"] = [3]string{"one", "two", "three"} 13 | 14 | // run the Fib function b.N times 15 | for n := 0; n < b.N; n++ { 16 | colorjson.Marshal(simpleMap) 17 | } 18 | } 19 | 20 | func benchmarkPrettyJSON(i int, b *testing.B) { 21 | simpleMap := make(map[string]interface{}) 22 | simpleMap["a"] = 1 23 | simpleMap["b"] = "bee" 24 | simpleMap["c"] = [3]float64{1, 2, 3} 25 | simpleMap["d"] = [3]string{"one", "two", "three"} 26 | 27 | // run the Fib function b.N times 28 | for n := 0; n < b.N; n++ { 29 | prettyjson.Marshal(simpleMap) 30 | } 31 | } 32 | 33 | func BenchmarkMarshall(b *testing.B) { benchmarkMarshall(100, b) } 34 | func BenchmarkMarshall1k(b *testing.B) { benchmarkMarshall(1000, b) } 35 | func BenchmarkPrettyJSON(b *testing.B) { benchmarkPrettyJSON(100, b) } 36 | func BenchmarkPrettyJSON1k(b *testing.B) { benchmarkPrettyJSON(1000, b) } 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ColorJSON: The Fast Color JSON Marshaller for Go 2 | ================================================ 3 | ![ColorJSON Output](https://i.imgur.com/pLtCXhb.png) 4 | What is this? 5 | ------------- 6 | 7 | This package is based heavily on hokaccha/go-prettyjson but has some noticible differences: 8 | - Over twice as fast (recursive descent serialization uses buffer instead of string concatenation) 9 | ``` 10 | BenchmarkColorJSONMarshall-4 500000 2498 ns/op 11 | BenchmarkPrettyJSON-4 200000 6145 ns/op 12 | ``` 13 | - more customizable (ability to have zero indent, print raw json strings, etc...) 14 | - better defaults (less bold colors) 15 | 16 | ColorJSON was built in order to produce fast beautiful colorized JSON output for [Saw](http://github.com/TylerBrock/saw). 17 | 18 | Installation 19 | ------------ 20 | 21 | ```sh 22 | go get -u github.com/TylerBrock/colorjson 23 | ``` 24 | 25 | Usage 26 | ----- 27 | 28 | Setup 29 | 30 | ```go 31 | import "github.com/TylerBrock/colorjson" 32 | 33 | str := `{ 34 | "str": "foo", 35 | "num": 100, 36 | "bool": false, 37 | "null": null, 38 | "array": ["foo", "bar", "baz"], 39 | "obj": { "a": 1, "b": 2 } 40 | }` 41 | 42 | // Create an intersting JSON object to marshal in a pretty format 43 | var obj map[string]interface{} 44 | json.Unmarshal([]byte(str), &obj) 45 | ``` 46 | 47 | Vanilla Usage 48 | 49 | ```go 50 | s, _ := colorjson.Marshal(obj) 51 | fmt.Println(string(s)) 52 | ``` 53 | 54 | Customization (Custom Indent) 55 | ```go 56 | f := colorjson.NewFormatter() 57 | f.Indent = 2 58 | 59 | s, _ := f.Marshal(v) 60 | fmt.Println(string(s)) 61 | ``` 62 | -------------------------------------------------------------------------------- /colorjson.go: -------------------------------------------------------------------------------- 1 | package colorjson 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "sort" 8 | "strconv" 9 | "strings" 10 | 11 | "github.com/fatih/color" 12 | ) 13 | 14 | const initialDepth = 0 15 | const valueSep = "," 16 | const null = "null" 17 | const startMap = "{" 18 | const endMap = "}" 19 | const startArray = "[" 20 | const endArray = "]" 21 | 22 | const emptyMap = startMap + endMap 23 | const emptyArray = startArray + endArray 24 | 25 | type Formatter struct { 26 | KeyColor *color.Color 27 | StringColor *color.Color 28 | BoolColor *color.Color 29 | NumberColor *color.Color 30 | NullColor *color.Color 31 | StringMaxLength int 32 | Indent int 33 | DisabledColor bool 34 | RawStrings bool 35 | } 36 | 37 | func NewFormatter() *Formatter { 38 | return &Formatter{ 39 | KeyColor: color.New(color.FgWhite), 40 | StringColor: color.New(color.FgGreen), 41 | BoolColor: color.New(color.FgYellow), 42 | NumberColor: color.New(color.FgCyan), 43 | NullColor: color.New(color.FgMagenta), 44 | StringMaxLength: 0, 45 | DisabledColor: false, 46 | Indent: 0, 47 | RawStrings: false, 48 | } 49 | } 50 | 51 | func (f *Formatter) sprintfColor(c *color.Color, format string, args ...interface{}) string { 52 | if f.DisabledColor || c == nil { 53 | return fmt.Sprintf(format, args...) 54 | } 55 | return c.SprintfFunc()(format, args...) 56 | } 57 | 58 | func (f *Formatter) sprintColor(c *color.Color, s string) string { 59 | if f.DisabledColor || c == nil { 60 | return fmt.Sprint(s) 61 | } 62 | return c.SprintFunc()(s) 63 | } 64 | 65 | func (f *Formatter) writeIndent(buf *bytes.Buffer, depth int) { 66 | buf.WriteString(strings.Repeat(" ", f.Indent*depth)) 67 | } 68 | 69 | func (f *Formatter) writeObjSep(buf *bytes.Buffer) { 70 | if f.Indent != 0 { 71 | buf.WriteByte('\n') 72 | } else { 73 | buf.WriteByte(' ') 74 | } 75 | } 76 | 77 | func (f *Formatter) Marshal(jsonObj interface{}) ([]byte, error) { 78 | buffer := bytes.Buffer{} 79 | f.marshalValue(jsonObj, &buffer, initialDepth) 80 | return buffer.Bytes(), nil 81 | } 82 | 83 | func (f *Formatter) marshalMap(m map[string]interface{}, buf *bytes.Buffer, depth int) { 84 | remaining := len(m) 85 | 86 | if remaining == 0 { 87 | buf.WriteString(emptyMap) 88 | return 89 | } 90 | 91 | keys := make([]string, 0) 92 | for key := range m { 93 | keys = append(keys, key) 94 | } 95 | 96 | sort.Strings(keys) 97 | 98 | buf.WriteString(startMap) 99 | f.writeObjSep(buf) 100 | 101 | for _, key := range keys { 102 | f.writeIndent(buf, depth+1) 103 | buf.WriteString(f.KeyColor.Sprintf("\"%s\": ", key)) 104 | f.marshalValue(m[key], buf, depth+1) 105 | remaining-- 106 | if remaining != 0 { 107 | buf.WriteString(valueSep) 108 | } 109 | f.writeObjSep(buf) 110 | } 111 | f.writeIndent(buf, depth) 112 | buf.WriteString(endMap) 113 | } 114 | 115 | func (f *Formatter) marshalArray(a []interface{}, buf *bytes.Buffer, depth int) { 116 | if len(a) == 0 { 117 | buf.WriteString(emptyArray) 118 | return 119 | } 120 | 121 | buf.WriteString(startArray) 122 | f.writeObjSep(buf) 123 | 124 | for i, v := range a { 125 | f.writeIndent(buf, depth+1) 126 | f.marshalValue(v, buf, depth+1) 127 | if i < len(a)-1 { 128 | buf.WriteString(valueSep) 129 | } 130 | f.writeObjSep(buf) 131 | } 132 | f.writeIndent(buf, depth) 133 | buf.WriteString(endArray) 134 | } 135 | 136 | func (f *Formatter) marshalValue(val interface{}, buf *bytes.Buffer, depth int) { 137 | switch v := val.(type) { 138 | case map[string]interface{}: 139 | f.marshalMap(v, buf, depth) 140 | case []interface{}: 141 | f.marshalArray(v, buf, depth) 142 | case string: 143 | f.marshalString(v, buf) 144 | case float64: 145 | buf.WriteString(f.sprintColor(f.NumberColor, strconv.FormatFloat(v, 'f', -1, 64))) 146 | case bool: 147 | buf.WriteString(f.sprintColor(f.BoolColor, (strconv.FormatBool(v)))) 148 | case nil: 149 | buf.WriteString(f.sprintColor(f.NullColor, null)) 150 | case json.Number: 151 | buf.WriteString(f.sprintColor(f.NumberColor, v.String())) 152 | } 153 | } 154 | 155 | func (f *Formatter) marshalString(str string, buf *bytes.Buffer) { 156 | if !f.RawStrings { 157 | strBytes, _ := json.Marshal(str) 158 | str = string(strBytes) 159 | } 160 | 161 | if f.StringMaxLength != 0 && len(str) >= f.StringMaxLength { 162 | str = fmt.Sprintf("%s...", str[0:f.StringMaxLength]) 163 | } 164 | 165 | buf.WriteString(f.sprintColor(f.StringColor, str)) 166 | } 167 | 168 | // Marshal JSON data with default options 169 | func Marshal(jsonObj interface{}) ([]byte, error) { 170 | return NewFormatter().Marshal(jsonObj) 171 | } 172 | --------------------------------------------------------------------------------