cuelang.org/go@v0.13.0/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 "cuelang.org/go/internal/core/adt" 31 internaljson "cuelang.org/go/internal/encoding/json" 32 "cuelang.org/go/internal/pkg" 33 "cuelang.org/go/internal/value" 34 ) 35 36 // Compact generates the JSON-encoded src with insignificant space characters 37 // elided. 38 func Compact(src []byte) (string, error) { 39 dst := bytes.Buffer{} 40 if err := json.Compact(&dst, src); err != nil { 41 return "", err 42 } 43 return dst.String(), nil 44 } 45 46 // Indent creates an indented form of the JSON-encoded src. 47 // Each element in a JSON object or array begins on a new, 48 // indented line beginning with prefix followed by one or more 49 // copies of indent according to the indentation nesting. 50 // The data appended to dst does not begin with the prefix nor 51 // any indentation, to make it easier to embed inside other formatted JSON data. 52 // Although leading space characters (space, tab, carriage return, newline) 53 // at the beginning of src are dropped, trailing space characters 54 // at the end of src are preserved and copied to dst. 55 // For example, if src has no trailing spaces, neither will dst; 56 // if src ends in a trailing newline, so will dst. 57 func Indent(src []byte, prefix, indent string) (string, error) { 58 dst := bytes.Buffer{} 59 if err := json.Indent(&dst, src, prefix, indent); err != nil { 60 return "", err 61 } 62 return dst.String(), nil 63 } 64 65 // HTMLEscape returns the JSON-encoded src with <, >, &, U+2028 and 66 // U+2029 characters inside string literals changed to \u003c, \u003e, \u0026, 67 // \u2028, \u2029 so that the JSON will be safe to embed inside HTML <script> 68 // tags. For historical reasons, web browsers don't honor standard HTML escaping 69 // within <script> tags, so an alternative JSON encoding must be used. 70 func HTMLEscape(src []byte) string { 71 dst := &bytes.Buffer{} 72 json.HTMLEscape(dst, src) 73 return dst.String() 74 } 75 76 // Marshal returns the JSON encoding of v. 77 func Marshal(v cue.Value) (string, error) { 78 b, err := internaljson.Marshal(v) 79 return string(b), err 80 } 81 82 // MarshalStream turns a list into a stream of JSON objects. 83 func MarshalStream(v cue.Value) (string, error) { 84 // TODO: return an io.Reader and allow asynchronous processing. 85 iter, err := v.List() 86 if err != nil { 87 return "", err 88 } 89 var b strings.Builder 90 for iter.Next() { 91 p, err := internaljson.Marshal(iter.Value()) 92 if err != nil { 93 return "", err 94 } 95 b.Write(p) 96 b.WriteByte('\n') 97 } 98 return b.String(), nil 99 } 100 101 // UnmarshalStream parses the JSON to a CUE instance. 102 func UnmarshalStream(data []byte) (ast.Expr, error) { 103 d := cuejson.NewDecoder(nil, "", bytes.NewReader(data)) 104 105 a := []ast.Expr{} 106 for { 107 x, err := d.Extract() 108 if err == io.EOF { 109 break 110 } 111 if err != nil { 112 return nil, err 113 } 114 a = append(a, x) 115 } 116 117 return ast.NewList(a...), nil 118 } 119 120 // Unmarshal parses the JSON-encoded data. 121 func Unmarshal(b []byte) (ast.Expr, error) { 122 if !json.Valid(b) { 123 return nil, fmt.Errorf("json: invalid JSON") 124 } 125 expr, err := parser.ParseExpr("json", b) 126 if err != nil { 127 // NOTE: should never happen. 128 return nil, errors.Wrapf(err, token.NoPos, "json: could not parse JSON") 129 } 130 return expr, nil 131 } 132 133 // Validate validates JSON and confirms it matches the constraints 134 // specified by v. 135 func Validate(b []byte, v pkg.Schema) (bool, error) { 136 c := value.OpContext(v) 137 return validate(c, b, v) 138 } 139 140 // validate is the actual implementation of Validate. 141 func validate(c *adt.OpContext, b []byte, v pkg.Schema) (bool, error) { 142 if !json.Valid(b) { 143 return false, fmt.Errorf("json: invalid JSON") 144 } 145 v2 := v.Context().CompileBytes(b, cue.Filename("json.Validate")) 146 if err := v2.Err(); err != nil { 147 return false, err 148 } 149 150 vx := adt.Unify(c, value.Vertex(v2), value.Vertex(v)) 151 v = value.Make(c, vx) 152 if err := v.Err(); err != nil { 153 return false, err 154 } 155 156 if err := v.Validate(cue.Final()); err != nil { 157 return false, err 158 } 159 160 return true, nil 161 }