pax_global_header00006660000000000000000000000064140120025560014505gustar00rootroot0000000000000052 comment=c8480cec0eeedc054ad6e351920ee0edd483ac74 pretty-1.0.5/000077500000000000000000000000001401200255600130375ustar00rootroot00000000000000pretty-1.0.5/.github/000077500000000000000000000000001401200255600143775ustar00rootroot00000000000000pretty-1.0.5/.github/workflows/000077500000000000000000000000001401200255600164345ustar00rootroot00000000000000pretty-1.0.5/.github/workflows/go.yml000066400000000000000000000012221401200255600175610ustar00rootroot00000000000000name: Go on: push: branches: [ master ] pull_request: branches: [ master ] jobs: build: name: Build runs-on: ubuntu-latest steps: - name: Set up Go 1.x uses: actions/setup-go@v2 with: go-version: ^1.13 - name: Check out code into the Go module directory uses: actions/checkout@v2 - name: Get dependencies run: | go get -v -t -d ./... if [ -f Gopkg.toml ]; then curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh dep ensure fi - name: Build run: go build -v . - name: Test run: go test -v . pretty-1.0.5/LICENSE000066400000000000000000000020651401200255600140470ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2017 Josh Baker Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. pretty-1.0.5/README.md000066400000000000000000000054741401200255600143300ustar00rootroot00000000000000# Pretty [![GoDoc](https://img.shields.io/badge/api-reference-blue.svg?style=flat-square)](https://pkg.go.dev/github.com/tidwall/pretty) Pretty is a Go package that provides [fast](#performance) methods for formatting JSON for human readability, or to compact JSON for smaller payloads. Getting Started =============== ## Installing To start using Pretty, install Go and run `go get`: ```sh $ go get -u github.com/tidwall/pretty ``` This will retrieve the library. ## Pretty Using this example: ```json {"name": {"first":"Tom","last":"Anderson"}, "age":37, "children": ["Sara","Alex","Jack"], "fav.movie": "Deer Hunter", "friends": [ {"first": "Janet", "last": "Murphy", "age": 44} ]} ``` The following code: ```go result = pretty.Pretty(example) ``` Will format the json to: ```json { "name": { "first": "Tom", "last": "Anderson" }, "age": 37, "children": ["Sara", "Alex", "Jack"], "fav.movie": "Deer Hunter", "friends": [ { "first": "Janet", "last": "Murphy", "age": 44 } ] } ``` ## Color Color will colorize the json for outputing to the screen. ```json result = pretty.Color(json, nil) ``` Will add color to the result for printing to the terminal. The second param is used for a customizing the style, and passing nil will use the default `pretty.TerminalStyle`. ## Ugly The following code: ```go result = pretty.Ugly(example) ``` Will format the json to: ```json {"name":{"first":"Tom","last":"Anderson"},"age":37,"children":["Sara","Alex","Jack"],"fav.movie":"Deer Hunter","friends":[{"first":"Janet","last":"Murphy","age":44}]}``` ``` ## Customized output There's a `PrettyOptions(json, opts)` function which allows for customizing the output with the following options: ```go type Options struct { // Width is an max column width for single line arrays // Default is 80 Width int // Prefix is a prefix for all lines // Default is an empty string Prefix string // Indent is the nested indentation // Default is two spaces Indent string // SortKeys will sort the keys alphabetically // Default is false SortKeys bool } ``` ## Performance Benchmarks of Pretty alongside the builtin `encoding/json` Indent/Compact methods. ``` BenchmarkPretty-8 1000000 1283 ns/op 720 B/op 2 allocs/op BenchmarkUgly-8 3000000 426 ns/op 240 B/op 1 allocs/op BenchmarkUglyInPlace-8 5000000 340 ns/op 0 B/op 0 allocs/op BenchmarkJSONIndent-8 300000 4628 ns/op 1069 B/op 4 allocs/op BenchmarkJSONCompact-8 1000000 2469 ns/op 758 B/op 4 allocs/op ``` *These benchmarks were run on a MacBook Pro 15" 2.8 GHz Intel Core i7 using Go 1.7.* ## Contact Josh Baker [@tidwall](http://twitter.com/tidwall) ## License Pretty source code is available under the MIT [License](/LICENSE). pretty-1.0.5/pretty.go000066400000000000000000000266311401200255600147250ustar00rootroot00000000000000package pretty import ( "sort" ) // Options is Pretty options type Options struct { // Width is an max column width for single line arrays // Default is 80 Width int // Prefix is a prefix for all lines // Default is an empty string Prefix string // Indent is the nested indentation // Default is two spaces Indent string // SortKeys will sort the keys alphabetically // Default is false SortKeys bool } // DefaultOptions is the default options for pretty formats. var DefaultOptions = &Options{Width: 80, Prefix: "", Indent: " ", SortKeys: false} // Pretty converts the input json into a more human readable format where each // element is on it's own line with clear indentation. func Pretty(json []byte) []byte { return PrettyOptions(json, nil) } // PrettyOptions is like Pretty but with customized options. func PrettyOptions(json []byte, opts *Options) []byte { if opts == nil { opts = DefaultOptions } buf := make([]byte, 0, len(json)) if len(opts.Prefix) != 0 { buf = append(buf, opts.Prefix...) } buf, _, _, _ = appendPrettyAny(buf, json, 0, true, opts.Width, opts.Prefix, opts.Indent, opts.SortKeys, 0, 0, -1) if len(buf) > 0 { buf = append(buf, '\n') } return buf } // Ugly removes insignificant space characters from the input json byte slice // and returns the compacted result. func Ugly(json []byte) []byte { buf := make([]byte, 0, len(json)) return ugly(buf, json) } // UglyInPlace removes insignificant space characters from the input json // byte slice and returns the compacted result. This method reuses the // input json buffer to avoid allocations. Do not use the original bytes // slice upon return. func UglyInPlace(json []byte) []byte { return ugly(json, json) } func ugly(dst, src []byte) []byte { dst = dst[:0] for i := 0; i < len(src); i++ { if src[i] > ' ' { dst = append(dst, src[i]) if src[i] == '"' { for i = i + 1; i < len(src); i++ { dst = append(dst, src[i]) if src[i] == '"' { j := i - 1 for ; ; j-- { if src[j] != '\\' { break } } if (j-i)%2 != 0 { break } } } } } } return dst } func appendPrettyAny(buf, json []byte, i int, pretty bool, width int, prefix, indent string, sortkeys bool, tabs, nl, max int) ([]byte, int, int, bool) { for ; i < len(json); i++ { if json[i] <= ' ' { continue } if json[i] == '"' { return appendPrettyString(buf, json, i, nl) } if (json[i] >= '0' && json[i] <= '9') || json[i] == '-' { return appendPrettyNumber(buf, json, i, nl) } if json[i] == '{' { return appendPrettyObject(buf, json, i, '{', '}', pretty, width, prefix, indent, sortkeys, tabs, nl, max) } if json[i] == '[' { return appendPrettyObject(buf, json, i, '[', ']', pretty, width, prefix, indent, sortkeys, tabs, nl, max) } switch json[i] { case 't': return append(buf, 't', 'r', 'u', 'e'), i + 4, nl, true case 'f': return append(buf, 'f', 'a', 'l', 's', 'e'), i + 5, nl, true case 'n': return append(buf, 'n', 'u', 'l', 'l'), i + 4, nl, true } } return buf, i, nl, true } type pair struct { kstart, kend int vstart, vend int } type byKeyVal struct { sorted bool json []byte pairs []pair } func (arr *byKeyVal) Len() int { return len(arr.pairs) } func (arr *byKeyVal) Less(i, j int) bool { key1 := arr.json[arr.pairs[i].kstart+1 : arr.pairs[i].kend-1] key2 := arr.json[arr.pairs[j].kstart+1 : arr.pairs[j].kend-1] if string(key1) < string(key2) { return true } if string(key1) > string(key2) { return false } return arr.pairs[i].vstart < arr.pairs[j].vstart } func (arr *byKeyVal) Swap(i, j int) { arr.pairs[i], arr.pairs[j] = arr.pairs[j], arr.pairs[i] arr.sorted = true } func appendPrettyObject(buf, json []byte, i int, open, close byte, pretty bool, width int, prefix, indent string, sortkeys bool, tabs, nl, max int) ([]byte, int, int, bool) { var ok bool if width > 0 { if pretty && open == '[' && max == -1 { // here we try to create a single line array max := width - (len(buf) - nl) if max > 3 { s1, s2 := len(buf), i buf, i, _, ok = appendPrettyObject(buf, json, i, '[', ']', false, width, prefix, "", sortkeys, 0, 0, max) if ok && len(buf)-s1 <= max { return buf, i, nl, true } buf = buf[:s1] i = s2 } } else if max != -1 && open == '{' { return buf, i, nl, false } } buf = append(buf, open) i++ var pairs []pair if open == '{' && sortkeys { pairs = make([]pair, 0, 8) } var n int for ; i < len(json); i++ { if json[i] <= ' ' { continue } if json[i] == close { if pretty { if open == '{' && sortkeys { buf = sortPairs(json, buf, pairs) } if n > 0 { nl = len(buf) if buf[nl-1] == ' ' { buf[nl-1] = '\n' } else { buf = append(buf, '\n') } } if buf[len(buf)-1] != open { buf = appendTabs(buf, prefix, indent, tabs) } } buf = append(buf, close) return buf, i + 1, nl, open != '{' } if open == '[' || json[i] == '"' { if n > 0 { buf = append(buf, ',') if width != -1 && open == '[' { buf = append(buf, ' ') } } var p pair if pretty { nl = len(buf) if buf[nl-1] == ' ' { buf[nl-1] = '\n' } else { buf = append(buf, '\n') } if open == '{' && sortkeys { p.kstart = i p.vstart = len(buf) } buf = appendTabs(buf, prefix, indent, tabs+1) } if open == '{' { buf, i, nl, _ = appendPrettyString(buf, json, i, nl) if sortkeys { p.kend = i } buf = append(buf, ':') if pretty { buf = append(buf, ' ') } } buf, i, nl, ok = appendPrettyAny(buf, json, i, pretty, width, prefix, indent, sortkeys, tabs+1, nl, max) if max != -1 && !ok { return buf, i, nl, false } if pretty && open == '{' && sortkeys { p.vend = len(buf) if p.kstart > p.kend || p.vstart > p.vend { // bad data. disable sorting sortkeys = false } else { pairs = append(pairs, p) } } i-- n++ } } return buf, i, nl, open != '{' } func sortPairs(json, buf []byte, pairs []pair) []byte { if len(pairs) == 0 { return buf } vstart := pairs[0].vstart vend := pairs[len(pairs)-1].vend arr := byKeyVal{false, json, pairs} sort.Stable(&arr) if !arr.sorted { return buf } nbuf := make([]byte, 0, vend-vstart) for i, p := range pairs { nbuf = append(nbuf, buf[p.vstart:p.vend]...) if i < len(pairs)-1 { nbuf = append(nbuf, ',') nbuf = append(nbuf, '\n') } } return append(buf[:vstart], nbuf...) } func appendPrettyString(buf, json []byte, i, nl int) ([]byte, int, int, bool) { s := i i++ for ; i < len(json); i++ { if json[i] == '"' { var sc int for j := i - 1; j > s; j-- { if json[j] == '\\' { sc++ } else { break } } if sc%2 == 1 { continue } i++ break } } return append(buf, json[s:i]...), i, nl, true } func appendPrettyNumber(buf, json []byte, i, nl int) ([]byte, int, int, bool) { s := i i++ for ; i < len(json); i++ { if json[i] <= ' ' || json[i] == ',' || json[i] == ':' || json[i] == ']' || json[i] == '}' { break } } return append(buf, json[s:i]...), i, nl, true } func appendTabs(buf []byte, prefix, indent string, tabs int) []byte { if len(prefix) != 0 { buf = append(buf, prefix...) } if len(indent) == 2 && indent[0] == ' ' && indent[1] == ' ' { for i := 0; i < tabs; i++ { buf = append(buf, ' ', ' ') } } else { for i := 0; i < tabs; i++ { buf = append(buf, indent...) } } return buf } // Style is the color style type Style struct { Key, String, Number [2]string True, False, Null [2]string Escape [2]string Append func(dst []byte, c byte) []byte } func hexp(p byte) byte { switch { case p < 10: return p + '0' default: return (p - 10) + 'a' } } // TerminalStyle is for terminals var TerminalStyle *Style func init() { TerminalStyle = &Style{ Key: [2]string{"\x1B[94m", "\x1B[0m"}, String: [2]string{"\x1B[92m", "\x1B[0m"}, Number: [2]string{"\x1B[93m", "\x1B[0m"}, True: [2]string{"\x1B[96m", "\x1B[0m"}, False: [2]string{"\x1B[96m", "\x1B[0m"}, Null: [2]string{"\x1B[91m", "\x1B[0m"}, Escape: [2]string{"\x1B[35m", "\x1B[0m"}, Append: func(dst []byte, c byte) []byte { if c < ' ' && (c != '\r' && c != '\n' && c != '\t' && c != '\v') { dst = append(dst, "\\u00"...) dst = append(dst, hexp((c>>4)&0xF)) return append(dst, hexp((c)&0xF)) } return append(dst, c) }, } } // Color will colorize the json. The style parma is used for customizing // the colors. Passing nil to the style param will use the default // TerminalStyle. func Color(src []byte, style *Style) []byte { if style == nil { style = TerminalStyle } apnd := style.Append if apnd == nil { apnd = func(dst []byte, c byte) []byte { return append(dst, c) } } type stackt struct { kind byte key bool } var dst []byte var stack []stackt for i := 0; i < len(src); i++ { if src[i] == '"' { key := len(stack) > 0 && stack[len(stack)-1].key if key { dst = append(dst, style.Key[0]...) } else { dst = append(dst, style.String[0]...) } dst = apnd(dst, '"') esc := false uesc := 0 for i = i + 1; i < len(src); i++ { if src[i] == '\\' { if key { dst = append(dst, style.Key[1]...) } else { dst = append(dst, style.String[1]...) } dst = append(dst, style.Escape[0]...) dst = apnd(dst, src[i]) esc = true if i+1 < len(src) && src[i+1] == 'u' { uesc = 5 } else { uesc = 1 } } else if esc { dst = apnd(dst, src[i]) if uesc == 1 { esc = false dst = append(dst, style.Escape[1]...) if key { dst = append(dst, style.Key[0]...) } else { dst = append(dst, style.String[0]...) } } else { uesc-- } } else { dst = apnd(dst, src[i]) } if src[i] == '"' { j := i - 1 for ; ; j-- { if src[j] != '\\' { break } } if (j-i)%2 != 0 { break } } } if esc { dst = append(dst, style.Escape[1]...) } else if key { dst = append(dst, style.Key[1]...) } else { dst = append(dst, style.String[1]...) } } else if src[i] == '{' || src[i] == '[' { stack = append(stack, stackt{src[i], src[i] == '{'}) dst = apnd(dst, src[i]) } else if (src[i] == '}' || src[i] == ']') && len(stack) > 0 { stack = stack[:len(stack)-1] dst = apnd(dst, src[i]) } else if (src[i] == ':' || src[i] == ',') && len(stack) > 0 && stack[len(stack)-1].kind == '{' { stack[len(stack)-1].key = !stack[len(stack)-1].key dst = apnd(dst, src[i]) } else { var kind byte if (src[i] >= '0' && src[i] <= '9') || src[i] == '-' { kind = '0' dst = append(dst, style.Number[0]...) } else if src[i] == 't' { kind = 't' dst = append(dst, style.True[0]...) } else if src[i] == 'f' { kind = 'f' dst = append(dst, style.False[0]...) } else if src[i] == 'n' { kind = 'n' dst = append(dst, style.Null[0]...) } else { dst = apnd(dst, src[i]) } if kind != 0 { for ; i < len(src); i++ { if src[i] <= ' ' || src[i] == ',' || src[i] == ':' || src[i] == ']' || src[i] == '}' { i-- break } dst = apnd(dst, src[i]) } if kind == '0' { dst = append(dst, style.Number[1]...) } else if kind == 't' { dst = append(dst, style.True[1]...) } else if kind == 'f' { dst = append(dst, style.False[1]...) } else if kind == 'n' { dst = append(dst, style.Null[1]...) } } } } return dst } pretty-1.0.5/pretty_test.go000066400000000000000000000324021401200255600157550ustar00rootroot00000000000000package pretty import ( "bytes" "encoding/json" "fmt" "math/rand" "reflect" "strings" "testing" "time" ) func j(js interface{}) string { var v interface{} if err := json.Unmarshal([]byte(fmt.Sprintf("%s", js)), &v); err != nil { fmt.Printf(">>%s<<\n", js) panic(err) } data, err := json.Marshal(v) if err != nil { panic(err) } return string(data) } var example1 = []byte(` { "name": { "last": "Sanders", "first": "Janet" }, "children": [ "Andy", "Carol", "Mike" ], "values": [ 10.10, true, false, null, "hello", {} ], "values2": {}, "values3": [], "deep": {"deep":{"deep":[1,2,3,4,5]}} } `) var example2 = `[ 0, 10, 10.10, true, false, null, "hello \" "]` func assertEqual(t *testing.T, a, b interface{}) { t.Helper() if !reflect.DeepEqual(a, b) { t.Fatalf("Not equal\n\t'%v'\n\t'%v'", a, b) } } func TestPretty(t *testing.T) { pretty := Pretty(Ugly(Pretty([]byte(example1)))) assertEqual(t, j(pretty), j(pretty)) assertEqual(t, j(example1), j(pretty)) pretty = Pretty(Ugly(Pretty([]byte(example2)))) assertEqual(t, j(pretty), j(pretty)) assertEqual(t, j(example2), j(pretty)) pretty = Pretty([]byte(" ")) assertEqual(t, "", string(pretty)) opts := *DefaultOptions opts.SortKeys = true pretty = PrettyOptions(Ugly(Pretty([]byte(example2))), &opts) assertEqual(t, j(pretty), j(pretty)) assertEqual(t, j(example2), j(pretty)) } func TestUgly(t *testing.T) { ugly := Ugly([]byte(example1)) var buf bytes.Buffer err := json.Compact(&buf, []byte(example1)) assertEqual(t, nil, err) assertEqual(t, buf.Bytes(), ugly) ugly = UglyInPlace(ugly) assertEqual(t, nil, err) assertEqual(t, buf.Bytes(), ugly) } func TestRandom(t *testing.T) { rand.Seed(time.Now().UnixNano()) for i := 0; i < 100000; i++ { b := make([]byte, 1024) rand.Read(b) Pretty(b) Ugly(b) } } func TestBig(t *testing.T) { json := `[ { "_id": "58d19e070f4898817162964a", "index": "", "guid": "65d46c3e-9d3a-4bfe-bab2-252f36a53c6b", "isActive": false, "balance": "$1,064.00", "picture": "http://placehold.it/32x32", "age": 37, "eyeColor": "brown", "name": "Chan Orr", "gender": "male", "company": "SURETECH", "email": "chanorr@suretech.com", "phone": "+1 (808) 496-3754", "address": "792 Bushwick Place, Glenbrook, Vermont, 9893", "about": "Amet consequat eu enim laboris cillum ad laboris in quis laboris reprehenderit. Eu deserunt occaecat dolore eu veniam non dolore et magna ex incididunt. Ea dolor laboris ex officia culpa laborum amet adipisicing laboris tempor magna elit mollit ad. Tempor ex aliqua mollit enim laboris sunt fugiat. Sint sunt ex est non dolore consectetur culpa ullamco id dolor nulla labore. Sunt duis fugiat cupidatat sunt deserunt qui aute elit consequat sint cupidatat. Consequat ullamco aliqua nulla velit tempor aute.\r\n", "registered": "2014-08-04T04:09:10 +07:00", "latitude": 80.707807, "longitude": 18.857548, "tags": [ "consectetur", "est", "cupidatat", "nisi", "incididunt", "aliqua", "ullamco" ], "friends": [ { "id": 0, "name": "Little Edwards" }, { "id": 1, "name": "Gay Johns" }, { "id": 2, "name": "Hoover Noble" } ], "greeting": "Hello, Chan Orr! You have 3 unread messages.", "favoriteFruit": "banana" }, { "_id": "58d19e07c2119248f8fa11ff", "index": "", "guid": "b362f0a0-d1ed-4b94-9d6b-213712620a20", "isActive": false, "balance": "$1,321.26", "picture": "http://placehold.it/32x32", "age": 28, "eyeColor": "blue", "name": "Molly Hyde", "gender": "female", "company": "QUALITEX", "email": "mollyhyde@qualitex.com", "phone": "+1 (849) 455-2934", "address": "440 Visitation Place, Bridgetown, Palau, 5053", "about": "Ipsum reprehenderit nulla est nostrud ad incididunt officia in commodo id esse id. Ullamco ullamco commodo mollit ut id cupidatat veniam nostrud minim duis qui sit. Occaecat esse nostrud velit qui non dolor proident. Ipsum ipsum anim non mollit minim voluptate amet irure in. Sunt commodo occaecat aute ullamco sunt fugiat laboris culpa Lorem anim. Aliquip tempor excepteur labore aute deserunt consectetur incididunt aute eu est ullamco consectetur excepteur. Sunt sint consequat cupidatat nisi exercitation minim enim occaecat esse ex amet ex non.\r\n", "registered": "2014-09-12T08:51:11 +07:00", "latitude": 15.867177, "longitude": 165.862595, "tags": [ "enim", "sint", "elit", "laborum", "elit", "cupidatat", "ipsum" ], "friends": [ { "id": 0, "name": "Holmes Hurley" }, { "id": 1, "name": "Rhoda Spencer" }, { "id": 2, "name": "Tommie Gallegos" } ], "greeting": "Hello, Molly Hyde! You have 10 unread messages.", "favoriteFruit": "banana" }, { "_id": "58d19e07fc27eedd9159d710", "index": "", "guid": "1d343fd3-44f7-4246-a5e6-a9297afb3146", "isActive": false, "balance": "$1,459.65", "picture": "http://placehold.it/32x32", "age": 26, "eyeColor": "brown", "name": "Jaime Kennedy", "gender": "female", "company": "RECRITUBE", "email": "jaimekennedy@recritube.com", "phone": "+1 (983) 483-3522", "address": "997 Vanderveer Street, Alamo, Marshall Islands, 4767", "about": "Qui consequat veniam ex enim excepteur aliqua dolor duis Lorem deserunt. Lorem occaecat laboris quis nisi Lorem aute exercitation consectetur officia velit aliqua aliquip commodo. Tempor irure ad ipsum aliquip. Incididunt mollit aute cillum non magna duis officia anim laboris deserunt voluptate.\r\n", "registered": "2015-08-31T06:51:25 +07:00", "latitude": -7.486839, "longitude": 57.659287, "tags": [ "veniam", "aliqua", "aute", "amet", "laborum", "quis", "sint" ], "friends": [ { "id": 0, "name": "Brown Christensen" }, { "id": 1, "name": "Robyn Whitehead" }, { "id": 2, "name": "Dolly Weaver" } ], "greeting": "Hello, Jaime Kennedy! You have 3 unread messages.", "favoriteFruit": "banana" }, { "_id": "58d19e0783c362da4b71240d", "index": "", "guid": "dbe60229-60d2-4879-82f3-d9aca0baaf6f", "isActive": false, "balance": "$3,221.63", "picture": "http://placehold.it/32x32", "age": 32, "eyeColor": "green", "name": "Cherie Vinson", "gender": "female", "company": "SLAX", "email": "cherievinson@slax.com", "phone": "+1 (905) 474-3132", "address": "563 Macdougal Street, Navarre, New York, 8733", "about": "Ad laborum et magna quis veniam duis magna consectetur mollit in minim non officia aliquip. Ullamco dolor qui consectetur adipisicing. Incididunt ad ad incididunt duis velit laboris. Reprehenderit ullamco magna quis exercitation excepteur nisi labore pariatur laborum consequat eu laboris amet velit. Et dolore aliqua proident sunt dolore incididunt dolore fugiat ipsum tempor occaecat.\r\n", "registered": "2015-03-19T08:48:47 +07:00", "latitude": -56.480034, "longitude": -59.894094, "tags": [ "irure", "commodo", "quis", "cillum", "quis", "nulla", "irure" ], "friends": [ { "id": 0, "name": "Danielle Mullins" }, { "id": 1, "name": "Maxine Peters" }, { "id": 2, "name": "Francine James" } ], "greeting": "Hello, Cherie Vinson! You have 1 unread messages.", "favoriteFruit": "apple" }, { "_id": "58d19e07b8f1ea8e3451870d", "index": "", "guid": "91fd9527-770c-4006-a0ed-64ca0d819199", "isActive": true, "balance": "$2,387.38", "picture": "http://placehold.it/32x32", "age": 37, "eyeColor": "blue", "name": "Glenna Hanson", "gender": "female", "company": "ACUMENTOR", "email": "glennahanson@acumentor.com", "phone": "+1 (965) 564-3926", "address": "323 Seigel Street, Rosedale, Florida, 2700", "about": "Commodo id ex velit nulla incididunt occaecat aliquip ullamco consequat est. Esse officia adipisicing magna et et incididunt sit deserunt ex mollit id. Laborum proident sit sit duis proident cillum irure aliquip et commodo.\r\n", "registered": "2014-06-29T02:48:04 +07:00", "latitude": -6.141759, "longitude": 155.991532, "tags": [ "amet", "pariatur", "culpa", "eu", "commodo", "magna", "excepteur" ], "friends": [ { "id": 0, "name": "Blanchard Blackburn" }, { "id": 1, "name": "Ayers Guy" }, { "id": 2, "name": "Powers Salinas" } ], "greeting": "Hello, Glenna Hanson! You have 4 unread messages.", "favoriteFruit": "strawberry" }, { "_id": "58d19e07f1ad063dac8b72dc", "index": "", "guid": "9b8c6cef-cfcd-4e6d-85e4-fe2e6920ec31", "isActive": true, "balance": "$1,828.58", "picture": "http://placehold.it/32x32", "age": 29, "eyeColor": "green", "name": "Hays Shields", "gender": "male", "company": "ISOLOGICA", "email": "haysshields@isologica.com", "phone": "+1 (882) 469-3201", "address": "574 Columbus Place, Singer, Georgia, 8716", "about": "Consectetur et adipisicing ad quis incididunt qui labore et ex elit esse. Ad elit officia ullamco dolor reprehenderit. Sunt nisi ullamco mollit incididunt consectetur nostrud anim adipisicing ullamco aliqua eiusmod ad. Et excepteur voluptate adipisicing velit id quis duis Lorem id deserunt esse irure Lorem. Est irure sint Lorem aliqua adipisicing velit irure Lorem. Ex in culpa laborum nostrud esse eu laboris velit. Anim excepteur ex ipsum amet nostrud cillum.\r\n", "registered": "2014-02-10T07:17:14 +07:00", "latitude": -66.354543, "longitude": 138.400461, "tags": [ "mollit", "labore", "id", "labore", "dolor", "in", "elit" ], "friends": [ { "id": 0, "name": "Mendoza Craig" }, { "id": 1, "name": "Rowena Carey" }, { "id": 2, "name": "Barry Francis" } ], "greeting": "Hello, Hays Shields! You have 10 unread messages.", "favoriteFruit": "strawberry" } ]` opts := *DefaultOptions opts.SortKeys = true jsonb := PrettyOptions(Ugly([]byte(json)), &opts) assertEqual(t, j(jsonb), j(json)) } func TestColor(t *testing.T) { res := Color(Pretty([]byte(` {"hello":"world","what":123, "arr":["1","2",1,2,true,false,null], "obj":{"key1":null,"ar`+"\x1B[36m"+`Cyanr2":[1,2,3,"123","456"]}} `)), nil) if string(res) != `{ "hello": "world", "what": 123, "arr": ["1", "2", 1, 2, true, false, null], "obj": { "key1": null, "ar\u001b[36mCyanr2": [1, 2, 3, "123", "456"] } } ` { t.Fatal("invalid output") } } func BenchmarkPretty(t *testing.B) { t.ReportAllocs() t.ResetTimer() for i := 0; i < t.N; i++ { Pretty(example1) } } func BenchmarkPrettySortKeys(t *testing.B) { opts := *DefaultOptions opts.SortKeys = true t.ReportAllocs() t.ResetTimer() for i := 0; i < t.N; i++ { PrettyOptions(example1, &opts) } } func BenchmarkUgly(t *testing.B) { t.ReportAllocs() t.ResetTimer() for i := 0; i < t.N; i++ { Ugly(example1) } } func BenchmarkUglyInPlace(t *testing.B) { example2 := []byte(string(example1)) t.ReportAllocs() t.ResetTimer() for i := 0; i < t.N; i++ { UglyInPlace(example2) } } func BenchmarkJSONIndent(t *testing.B) { var dst bytes.Buffer t.ReportAllocs() t.ResetTimer() for i := 0; i < t.N; i++ { json.Indent(&dst, example1, "", " ") } } func BenchmarkJSONCompact(t *testing.B) { var dst bytes.Buffer t.ReportAllocs() t.ResetTimer() for i := 0; i < t.N; i++ { json.Compact(&dst, example1) } } func TestPrettyNoSpaceAfterNewline(t *testing.T) { json := `[{"foo":1,"bar":2},{"foo":3,"bar":4}]` json = string(Pretty([]byte(json))) if strings.Index(json, " \n") != -1 { t.Fatal("found a space followed by a newline, which should not be allowed") } } func TestPrettyStableSort(t *testing.T) { json := `{"c":3,"b":3,"a":3,"c":2,"b":2,"a":2,"c":1,"b":1,"a":1}` opts := *DefaultOptions opts.SortKeys = true json = string(Ugly(PrettyOptions([]byte(json), &opts))) if json != `{"a":3,"a":2,"a":1,"b":3,"b":2,"b":1,"c":3,"c":2,"c":1}` { t.Fatal("out of order") } } func TestPrettyColor(t *testing.T) { json := `"abc\u0020def\nghi"` ret := string(Color([]byte(json), nil)) exp := "" + TerminalStyle.String[0] + `"abc` + TerminalStyle.String[1] + TerminalStyle.Escape[0] + `\u0020` + TerminalStyle.Escape[1] + TerminalStyle.String[0] + `def` + TerminalStyle.String[1] + TerminalStyle.Escape[0] + `\n` + TerminalStyle.Escape[1] + TerminalStyle.String[0] + `ghi"` + TerminalStyle.String[1] if ret != exp { t.Fatalf("expected '%s', got '%s'", exp, ret) } }