Source File
value.go
Belonging Package
encoding/json/jsontext
// Copyright 2020 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.//go:build goexperiment.jsonv2package jsontextimport ()// NOTE: Value is analogous to v1 json.RawMessage.// AppendFormat formats the JSON value in src and appends it to dst// according to the specified options.// See [Value.Format] for more details about the formatting behavior.//// The dst and src may overlap.// If an error is reported, then the entirety of src is appended to dst.func (, []byte, ...Options) ([]byte, error) {:= getBufferedEncoder(...)defer putBufferedEncoder().s.Flags.Set(jsonflags.OmitTopLevelNewline | 1)if := .s.WriteValue(); != nil {return append(, ...),}return append(, .s.Buf...), nil}// Value represents a single raw JSON value, which may be one of the following:// - a JSON literal (i.e., null, true, or false)// - a JSON string (e.g., "hello, world!")// - a JSON number (e.g., 123.456)// - an entire JSON object (e.g., {"fizz":"buzz"} )// - an entire JSON array (e.g., [1,2,3] )//// Value can represent entire array or object values, while [Token] cannot.// Value may contain leading and/or trailing whitespace.type Value []byte// Clone returns a copy of v.func ( Value) () Value {return bytes.Clone()}// String returns the string formatting of v.func ( Value) () string {if == nil {return "null"}return string()}// IsValid reports whether the raw JSON value is syntactically valid// according to the specified options.//// By default (if no options are specified), it validates according to RFC 7493.// It verifies whether the input is properly encoded as UTF-8,// that escape sequences within strings decode to valid Unicode codepoints, and// that all names in each object are unique.// It does not verify whether numbers are representable within the limits// of any common numeric type (e.g., float64, int64, or uint64).//// Relevant options include:// - [AllowDuplicateNames]// - [AllowInvalidUTF8]//// All other options are ignored.func ( Value) ( ...Options) bool {// TODO: Document support for [WithByteLimit] and [WithDepthLimit].:= getBufferedDecoder(, ...)defer putBufferedDecoder(), := .ReadValue(), := .ReadToken()return == nil && == io.EOF}// Format formats the raw JSON value in place.//// By default (if no options are specified), it validates according to RFC 7493// and produces the minimal JSON representation, where// all whitespace is elided and JSON strings use the shortest encoding.//// Relevant options include:// - [AllowDuplicateNames]// - [AllowInvalidUTF8]// - [EscapeForHTML]// - [EscapeForJS]// - [PreserveRawStrings]// - [CanonicalizeRawInts]// - [CanonicalizeRawFloats]// - [ReorderRawObjects]// - [SpaceAfterColon]// - [SpaceAfterComma]// - [Multiline]// - [WithIndent]// - [WithIndentPrefix]//// All other options are ignored.//// It is guaranteed to succeed if the value is valid according to the same options.// If the value is already formatted, then the buffer is not mutated.func ( *Value) ( ...Options) error {// TODO: Document support for [WithByteLimit] and [WithDepthLimit].return .format(, nil)}// format accepts two []Options to avoid the allocation appending them together.// It is equivalent to v.Format(append(opts1, opts2...)...).func ( *Value) (, []Options) error {:= getBufferedEncoder(...)defer putBufferedEncoder().s.Join(...).s.Flags.Set(jsonflags.OmitTopLevelNewline | 1)if := .s.WriteValue(*); != nil {return}if !bytes.Equal(*, .s.Buf) {* = append((*)[:0], .s.Buf...)}return nil}// Compact removes all whitespace from the raw JSON value.//// It does not reformat JSON strings or numbers to use any other representation.// To maximize the set of JSON values that can be formatted,// this permits values with duplicate names and invalid UTF-8.//// Compact is equivalent to calling [Value.Format] with the following options:// - [AllowDuplicateNames](true)// - [AllowInvalidUTF8](true)// - [PreserveRawStrings](true)//// Any options specified by the caller are applied after the initial set// and may deliberately override prior options.func ( *Value) ( ...Options) error {return .format([]Options{AllowDuplicateNames(true),AllowInvalidUTF8(true),PreserveRawStrings(true),}, )}// Indent reformats the whitespace in the raw JSON value so that each element// in a JSON object or array begins on a indented line according to the nesting.//// It does not reformat JSON strings or numbers to use any other representation.// To maximize the set of JSON values that can be formatted,// this permits values with duplicate names and invalid UTF-8.//// Indent is equivalent to calling [Value.Format] with the following options:// - [AllowDuplicateNames](true)// - [AllowInvalidUTF8](true)// - [PreserveRawStrings](true)// - [Multiline](true)//// Any options specified by the caller are applied after the initial set// and may deliberately override prior options.func ( *Value) ( ...Options) error {return .format([]Options{AllowDuplicateNames(true),AllowInvalidUTF8(true),PreserveRawStrings(true),Multiline(true),}, )}// Canonicalize canonicalizes the raw JSON value according to the// JSON Canonicalization Scheme (JCS) as defined by RFC 8785// where it produces a stable representation of a JSON value.//// JSON strings are formatted to use their minimal representation,// JSON numbers are formatted as double precision numbers according// to some stable serialization algorithm.// JSON object members are sorted in ascending order by name.// All whitespace is removed.//// The output stability is dependent on the stability of the application data// (see RFC 8785, Appendix E). It cannot produce stable output from// fundamentally unstable input. For example, if the JSON value// contains ephemeral data (e.g., a frequently changing timestamp),// then the value is still unstable regardless of whether this is called.//// Canonicalize is equivalent to calling [Value.Format] with the following options:// - [CanonicalizeRawInts](true)// - [CanonicalizeRawFloats](true)// - [ReorderRawObjects](true)//// Any options specified by the caller are applied after the initial set// and may deliberately override prior options.//// Note that JCS treats all JSON numbers as IEEE 754 double precision numbers.// Any numbers with precision beyond what is representable by that form// will lose their precision when canonicalized. For example, integer values// beyond ±2⁵³ will lose their precision. To preserve the original representation// of JSON integers, additionally set [CanonicalizeRawInts] to false://// v.Canonicalize(jsontext.CanonicalizeRawInts(false))func ( *Value) ( ...Options) error {return .format([]Options{CanonicalizeRawInts(true),CanonicalizeRawFloats(true),ReorderRawObjects(true),}, )}// MarshalJSON returns v as the JSON encoding of v.// It returns the stored value as the raw JSON output without any validation.// If v is nil, then this returns a JSON null.func ( Value) () ([]byte, error) {// NOTE: This matches the behavior of v1 json.RawMessage.MarshalJSON.if == nil {return []byte("null"), nil}return , nil}// UnmarshalJSON sets v as the JSON encoding of b.// It stores a copy of the provided raw JSON input without any validation.func ( *Value) ( []byte) error {// NOTE: This matches the behavior of v1 json.RawMessage.UnmarshalJSON.if == nil {return errors.New("jsontext.Value: UnmarshalJSON on nil pointer")}* = append((*)[:0], ...)return nil}// Kind returns the starting token kind.// For a valid value, this will never include '}' or ']'.func ( Value) () Kind {if := [jsonwire.ConsumeWhitespace():]; len() > 0 {return Kind([0]).normalize()}return invalidKind}const commaAndWhitespace = ", \n\r\t"type objectMember struct {// name is the unquoted name.name []byte // e.g., "name"// buffer is the entirety of the raw JSON object member// starting from right after the previous member (or opening '{')// until right after the member value.buffer []byte // e.g., `, \n\r\t"name": "value"`}func ( objectMember) ( objectMember) int {if := jsonwire.CompareUTF16(.name, .name); != 0 {return}// With [AllowDuplicateNames] or [AllowInvalidUTF8],// names could be identical, so also sort using the member value.return jsonwire.CompareUTF16(bytes.TrimLeft(.buffer, commaAndWhitespace),bytes.TrimLeft(.buffer, commaAndWhitespace))}var objectMemberPool = sync.Pool{New: func() any { return new([]objectMember) }}func getObjectMembers() *[]objectMember {:= objectMemberPool.Get().(*[]objectMember)* = (*)[:0]return}func putObjectMembers( *[]objectMember) {if cap(*) < 1<<10 {clear(*) // avoid pinning name and bufferobjectMemberPool.Put()}}// mustReorderObjects reorders in-place all object members in a JSON value,// which must be valid otherwise it panics.func mustReorderObjects( []byte) {// Obtain a buffered encoder just to use its internal buffer as// a scratch buffer for reordering object members.:= getBufferedEncoder()defer putBufferedEncoder()// Disable unnecessary checks to syntactically parse the JSON value.:= getBufferedDecoder()defer putBufferedDecoder().s.Flags.Set(jsonflags.AllowDuplicateNames | jsonflags.AllowInvalidUTF8 | 1)mustReorderObjectsFromDecoder(, &.s.Buf) // per RFC 8785, section 3.2.3}// mustReorderObjectsFromDecoder recursively reorders all object members in place// according to the ordering specified in RFC 8785, section 3.2.3.//// Pre-conditions:// - The value is valid (i.e., no decoder errors should ever occur).// - Initial call is provided a Decoder reading from the start of v.//// Post-conditions:// - Exactly one JSON value is read from the Decoder.// - All fully-parsed JSON objects are reordered by directly moving// the members in the value buffer.//// The runtime is approximately O(n·log(n)) + O(m·log(m)),// where n is len(v) and m is the total number of object members.func mustReorderObjectsFromDecoder( *Decoder, *[]byte) {switch , := .ReadToken(); .Kind() {case '{':// Iterate and collect the name and offsets for every object member.:= getObjectMembers()defer putObjectMembers()var objectMember:= true:= .InputOffset() // offset after '{'for .PeekKind() != '}' {:= .InputOffset()var jsonwire.ValueFlags, := .s.ReadValue(&)= jsonwire.UnquoteMayCopy(, .IsVerbatim())(, ):= .InputOffset():= objectMember{, .s.buf[:]}if && len(*) > 0 {= objectMember.Compare(, ) < 0}* = append(*, )=}:= .InputOffset() // offset before '}'.ReadToken()// Sort the members; return early if it's already sorted.if {return}:= (*)[0].bufferslices.SortFunc(*, objectMember.Compare):= (*)[0].buffer// Append the reordered members to a new buffer,// then copy the reordered members back over the original members.// Avoid swapping in place since each member may be a different size// where moving a member over a smaller member may corrupt the data// for subsequent members before they have been moved.//// The following invariant must hold:// sum([m.after-m.before for m in members]) == afterBody-beforeBody:= func( []byte) []byte {return [:len()-len(bytes.TrimLeft(, commaAndWhitespace))]}:= (*)[:0]for , := range * {switch {case == 0 && &.buffer[0] != &[0]:// First member after sorting is not the first member before sorting,// so use the prefix of the first member before sorting.= append(, ()...)= append(, bytes.TrimLeft(.buffer, commaAndWhitespace)...)case != 0 && &.buffer[0] == &[0]:// Later member after sorting is the first member before sorting,// so use the prefix of the first member after sorting.= append(, ()...)= append(, bytes.TrimLeft(.buffer, commaAndWhitespace)...)default:= append(, .buffer...)}}if int(-) != len() {panic("BUG: length invariant violated")}copy(.s.buf[:], )// Update scratch buffer to the largest amount ever used.if len() > len(*) {* =}case '[':for .PeekKind() != ']' {(, )}.ReadToken()default:if != nil {panic("BUG: " + .Error())}}}
![]() |
The pages are generated with Golds v0.7.9-preview. (GOOS=linux GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds. |