// Copyright 2015 go-swagger maintainers // // 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 jsonutils import ( "iter" "github.com/go-openapi/swag/jsonutils/adapters" "github.com/go-openapi/swag/typeutils" ) // JSONMapSlice represents a JSON object, with the order of keys maintained. // // It behaves like an ordered map, but keys can't be accessed in constant time. type JSONMapSlice []JSONMapItem // OrderedItems iterates over all (key,value) pairs with the order of keys maintained. // // This implements the [ifaces.Ordered] interface, so that [ifaces.Adapter] s know how to marshal // keys in the desired order. func (s JSONMapSlice) OrderedItems() iter.Seq2[string, any] { return func(yield func(string, any) bool) { for _, item := range s { if !yield(item.Key, item.Value) { return } } } } // SetOrderedItems sets keys in the [JSONMapSlice] objects, as presented by // the provided iterator. // // As a special case, if items is nil, this sets to receiver to a nil slice. // // This implements the [ifaces.SetOrdered] interface, so that [ifaces.Adapter] s know how to unmarshal // keys in the desired order. func (s *JSONMapSlice) SetOrderedItems(items iter.Seq2[string, any]) { if items == nil { // force receiver to be a nil slice *s = nil return } m := *s if len(m) > 0 { // update mode: short-circuited when unmarshaling fresh data structures idx := make(map[string]int, len(m)) for i, item := range m { idx[item.Key] = i } for k, v := range items { idx, ok := idx[k] if ok { m[idx].Value = v continue } m = append(m, JSONMapItem{Key: k, Value: v}) } *s = m return } for k, v := range items { m = append(m, JSONMapItem{Key: k, Value: v}) } *s = m } // MarshalJSON renders a [JSONMapSlice] as JSON bytes, preserving the order of keys. // // It will pick the JSON library currently configured by the [adapters.Registry] (defaults to the standard library). func (s JSONMapSlice) MarshalJSON() ([]byte, error) { orderedMarshaler := adapters.OrderedMarshalAdapterFor(s) defer orderedMarshaler.Redeem() return orderedMarshaler.OrderedMarshal(s) } // UnmarshalJSON builds a [JSONMapSlice] from JSON bytes, preserving the order of keys. // // Inner objects are unmarshaled as ordered [JSONMapSlice] slices and not map[string]any. // // It will pick the JSON library currently configured by the [adapters.Registry] (defaults to the standard library). func (s *JSONMapSlice) UnmarshalJSON(data []byte) error { if typeutils.IsNil(*s) { // allow to unmarshal with a simple var declaration (nil slice) *s = JSONMapSlice{} } orderedUnmarshaler := adapters.OrderedUnmarshalAdapterFor(s) defer orderedUnmarshaler.Redeem() return orderedUnmarshaler.OrderedUnmarshal(data, s) } // JSONMapItem represents the value of a key in a JSON object held by [JSONMapSlice]. // // Notice that JSONMapItem should not be marshaled to or unmarshaled from JSON directly. // // Use this type as part of a [JSONMapSlice] when dealing with JSON bytes. type JSONMapItem struct { Key string Value any }