github.com/ndau/noms@v1.0.5/go/util/json/to_json.go (about) 1 // Copyright 2019 Attic Labs, Inc. All rights reserved. 2 // Licensed under the Apache License, version 2.0: 3 // http://www.apache.org/licenses/LICENSE-2.0 4 5 package json 6 7 import ( 8 "encoding/json" 9 "errors" 10 "fmt" 11 "io" 12 13 "github.com/ndau/noms/go/types" 14 ) 15 16 // ToJSON encodes a Noms value as JSON. 17 func ToJSON(v types.Value, w io.Writer, opts ToOptions) error { 18 // TODO: This is a quick hack that is expedient. We should marshal directly to the writer without 19 // allocating a bunch of Go values. 20 p, err := toPile(v, opts) 21 if err != nil { 22 return err 23 } 24 25 enc := json.NewEncoder(w) 26 enc.SetIndent("", opts.Indent) 27 return enc.Encode(p) 28 } 29 30 // ToOptions controls how ToJSON works. 31 type ToOptions struct { 32 // Enable support for encoding Noms Lists. Lists are encoded as JSON arrays. 33 Lists bool 34 // Enable support for encoding Noms Maps. Maps are encoded as JSON objects. 35 Maps bool 36 // Enable support for encoding Noms Sets. Sets are encoded as JSON arrays. 37 Sets bool 38 // Enable support for encoding Noms Structs. Structs are encoded as JSON objects. 39 Structs bool 40 // String to use for indent when pretty-printing 41 Indent string 42 } 43 44 func toPile(v types.Value, opts ToOptions) (ret interface{}, err error) { 45 switch v := v.(type) { 46 case types.Bool: 47 return bool(v), nil 48 case types.Number: 49 return float64(v), nil 50 case types.String: 51 return string(v), nil 52 case types.Struct: 53 if !opts.Structs { 54 return nil, errors.New("Struct marshaling not enabled") 55 } 56 r := map[string]interface{}{} 57 if v.Name() != "" { 58 return nil, errors.New("Named struct marshaling not supported") 59 } 60 v.IterFields(func(k string, cv types.Value) (stop bool) { 61 var cp interface{} 62 cp, err = toPile(cv, opts) 63 if err != nil { 64 return true 65 } 66 r[k] = cp 67 return false 68 }) 69 return r, err 70 case types.Map: 71 if !opts.Maps { 72 return nil, errors.New("Map marshaling not enabled") 73 } 74 r := make(map[string]interface{}, v.Len()) 75 v.Iter(func(k, cv types.Value) (stop bool) { 76 sk, ok := k.(types.String) 77 if !ok { 78 err = fmt.Errorf("Map key kind %s not supported", types.KindToString[k.Kind()]) 79 return true 80 } 81 var cp interface{} 82 cp, err = toPile(cv, opts) 83 if err != nil { 84 return true 85 } 86 r[string(sk)] = cp 87 return false 88 }) 89 return r, err 90 case types.List: 91 if !opts.Lists { 92 return nil, errors.New("List marshaling not enabled") 93 } 94 r := make([]interface{}, v.Len()) 95 v.Iter(func(cv types.Value, i uint64) (stop bool) { 96 var cp interface{} 97 cp, err = toPile(cv, opts) 98 if err != nil { 99 return true 100 } 101 r[i] = cp 102 return false 103 }) 104 return r, err 105 case types.Set: 106 if !opts.Sets { 107 return nil, errors.New("Set marshaling not enabled") 108 } 109 r := make([]interface{}, 0, v.Len()) 110 v.Iter(func(cv types.Value) (stop bool) { 111 var cp interface{} 112 cp, err = toPile(cv, opts) 113 if err != nil { 114 return true 115 } 116 r = append(r, cp) 117 return false 118 }) 119 return r, err 120 } 121 return nil, fmt.Errorf("Unsupported kind: %s", types.KindToString[v.Kind()]) 122 }