| // Copyright 2018 The CUE Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package json |
| |
| import ( |
| "bytes" |
| "encoding/json" |
| "fmt" |
| |
| "cuelang.org/go/cue" |
| "cuelang.org/go/cue/ast" |
| "cuelang.org/go/cue/errors" |
| "cuelang.org/go/cue/parser" |
| "cuelang.org/go/cue/token" |
| "cuelang.org/go/internal/value" |
| ) |
| |
| // Compact generates the JSON-encoded src with insignificant space characters |
| // elided. |
| func Compact(src []byte) (string, error) { |
| dst := bytes.Buffer{} |
| if err := json.Compact(&dst, src); err != nil { |
| return "", err |
| } |
| return dst.String(), nil |
| } |
| |
| // Indent creates an indented form of the JSON-encoded src. |
| // Each element in a JSON object or array begins on a new, |
| // indented line beginning with prefix followed by one or more |
| // copies of indent according to the indentation nesting. |
| // The data appended to dst does not begin with the prefix nor |
| // any indentation, to make it easier to embed inside other formatted JSON data. |
| // Although leading space characters (space, tab, carriage return, newline) |
| // at the beginning of src are dropped, trailing space characters |
| // at the end of src are preserved and copied to dst. |
| // For example, if src has no trailing spaces, neither will dst; |
| // if src ends in a trailing newline, so will dst. |
| func Indent(src []byte, prefix, indent string) (string, error) { |
| dst := bytes.Buffer{} |
| if err := json.Indent(&dst, src, prefix, indent); err != nil { |
| return "", err |
| } |
| return dst.String(), nil |
| } |
| |
| // HTMLEscape returns the JSON-encoded src with <, >, &, U+2028 and |
| // U+2029 characters inside string literals changed to \u003c, \u003e, \u0026, |
| // \u2028, \u2029 so that the JSON will be safe to embed inside HTML <script> |
| // tags. For historical reasons, web browsers don't honor standard HTML escaping |
| // within <script> tags, so an alternative JSON encoding must be used. |
| func HTMLEscape(src []byte) string { |
| dst := &bytes.Buffer{} |
| json.HTMLEscape(dst, src) |
| return dst.String() |
| } |
| |
| // Marshal returns the JSON encoding of v. |
| func Marshal(v cue.Value) (string, error) { |
| b, err := json.Marshal(v) |
| return string(b), err |
| } |
| |
| // MarshalStream turns a list into a stream of JSON objects. |
| func MarshalStream(v cue.Value) (string, error) { |
| // TODO: return an io.Reader and allow asynchronous processing. |
| iter, err := v.List() |
| if err != nil { |
| return "", err |
| } |
| buf := &bytes.Buffer{} |
| for iter.Next() { |
| b, err := json.Marshal(iter.Value()) |
| if err != nil { |
| return "", err |
| } |
| buf.Write(b) |
| buf.WriteByte('\n') |
| } |
| return buf.String(), nil |
| } |
| |
| // Unmarshal parses the JSON-encoded data. |
| func Unmarshal(b []byte) (ast.Expr, error) { |
| if !json.Valid(b) { |
| return nil, fmt.Errorf("json: invalid JSON") |
| } |
| expr, err := parser.ParseExpr("json", b) |
| if err != nil { |
| // NOTE: should never happen. |
| return nil, errors.Wrapf(err, token.NoPos, "json: could not parse JSON") |
| } |
| return expr, nil |
| } |
| |
| // Validate validates JSON and confirms it matches the constraints |
| // specified by v. |
| func Validate(b []byte, v cue.Value) (bool, error) { |
| if !json.Valid(b) { |
| return false, fmt.Errorf("json: invalid JSON") |
| } |
| r := value.ConvertToRuntime(v.Context()) |
| inst, err := r.Compile("json.Validate", b) |
| if err != nil { |
| return false, err |
| } |
| |
| v = v.Unify(inst.Value()) |
| if v.Err() != nil { |
| return false, v.Err() |
| } |
| return true, nil |
| } |