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