cuelang.org/go@v0.10.1/pkg/encoding/json/manual.go (about) 1 // Copyright 2018 The CUE Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package json 16 17 import ( 18 "bytes" 19 "encoding/json" 20 "fmt" 21 "io" 22 "strings" 23 24 "cuelang.org/go/cue" 25 "cuelang.org/go/cue/ast" 26 "cuelang.org/go/cue/errors" 27 "cuelang.org/go/cue/parser" 28 "cuelang.org/go/cue/token" 29 cuejson "cuelang.org/go/encoding/json" 30 internaljson "cuelang.org/go/internal/encoding/json" 31 ) 32 33 // Compact generates the JSON-encoded src with insignificant space characters 34 // elided. 35 func Compact(src []byte) (string, error) { 36 dst := bytes.Buffer{} 37 if err := json.Compact(&dst, src); err != nil { 38 return "", err 39 } 40 return dst.String(), nil 41 } 42 43 // Indent creates an indented form of the JSON-encoded src. 44 // Each element in a JSON object or array begins on a new, 45 // indented line beginning with prefix followed by one or more 46 // copies of indent according to the indentation nesting. 47 // The data appended to dst does not begin with the prefix nor 48 // any indentation, to make it easier to embed inside other formatted JSON data. 49 // Although leading space characters (space, tab, carriage return, newline) 50 // at the beginning of src are dropped, trailing space characters 51 // at the end of src are preserved and copied to dst. 52 // For example, if src has no trailing spaces, neither will dst; 53 // if src ends in a trailing newline, so will dst. 54 func Indent(src []byte, prefix, indent string) (string, error) { 55 dst := bytes.Buffer{} 56 if err := json.Indent(&dst, src, prefix, indent); err != nil { 57 return "", err 58 } 59 return dst.String(), nil 60 } 61 62 // HTMLEscape returns the JSON-encoded src with <, >, &, U+2028 and 63 // U+2029 characters inside string literals changed to \u003c, \u003e, \u0026, 64 // \u2028, \u2029 so that the JSON will be safe to embed inside HTML <script> 65 // tags. For historical reasons, web browsers don't honor standard HTML escaping 66 // within <script> tags, so an alternative JSON encoding must be used. 67 func HTMLEscape(src []byte) string { 68 dst := &bytes.Buffer{} 69 json.HTMLEscape(dst, src) 70 return dst.String() 71 } 72 73 // Marshal returns the JSON encoding of v. 74 func Marshal(v cue.Value) (string, error) { 75 b, err := internaljson.Marshal(v) 76 return string(b), err 77 } 78 79 // MarshalStream turns a list into a stream of JSON objects. 80 func MarshalStream(v cue.Value) (string, error) { 81 // TODO: return an io.Reader and allow asynchronous processing. 82 iter, err := v.List() 83 if err != nil { 84 return "", err 85 } 86 var b strings.Builder 87 for iter.Next() { 88 p, err := internaljson.Marshal(iter.Value()) 89 if err != nil { 90 return "", err 91 } 92 b.Write(p) 93 b.WriteByte('\n') 94 } 95 return b.String(), nil 96 } 97 98 // UnmarshalStream parses the JSON to a CUE instance. 99 func UnmarshalStream(data []byte) (ast.Expr, error) { 100 var r cue.Runtime 101 d := cuejson.NewDecoder(&r, "", bytes.NewReader(data)) 102 103 a := []ast.Expr{} 104 for { 105 x, err := d.Extract() 106 if err == io.EOF { 107 break 108 } 109 if err != nil { 110 return nil, err 111 } 112 a = append(a, x) 113 } 114 115 return ast.NewList(a...), nil 116 } 117 118 // Unmarshal parses the JSON-encoded data. 119 func Unmarshal(b []byte) (ast.Expr, error) { 120 if !json.Valid(b) { 121 return nil, fmt.Errorf("json: invalid JSON") 122 } 123 expr, err := parser.ParseExpr("json", b) 124 if err != nil { 125 // NOTE: should never happen. 126 return nil, errors.Wrapf(err, token.NoPos, "json: could not parse JSON") 127 } 128 return expr, nil 129 } 130 131 // Validate validates JSON and confirms it matches the constraints 132 // specified by v. 133 func Validate(b []byte, v cue.Value) (bool, error) { 134 err := cuejson.Validate(b, v) 135 if err != nil { 136 return false, err 137 } 138 return true, nil 139 }