Module collection (3)
This commit is contained in:
parent
53d7e6c217
commit
6637db7be4
|
@ -0,0 +1,106 @@
|
|||
package json
|
||||
|
||||
import (
|
||||
j "encoding/json"
|
||||
"io"
|
||||
|
||||
. "gitea.zaclys.com/bvaudour/gob/option"
|
||||
)
|
||||
|
||||
// JsonDecoder permet de parser une entrée brute en un objet Json ou un tableau de Json.
|
||||
type JsonDecoder struct {
|
||||
d *j.Decoder
|
||||
}
|
||||
|
||||
// NewJsonDecoder retourne un décodeur pour la donnée d’entrée spécifiée.
|
||||
func NewJsonDecoder(r io.Reader) *JsonDecoder { return &JsonDecoder{d: j.NewDecoder(r)} }
|
||||
|
||||
// Decode retourne un objet Json décodé.
|
||||
func (dec *JsonDecoder) Decode() Result[Json] {
|
||||
o := make(Json)
|
||||
if err := dec.d.Decode(&o); err != nil {
|
||||
return Err[Json](err)
|
||||
}
|
||||
return Ok(o)
|
||||
}
|
||||
|
||||
// DecodeSlice retourne un tableau de Json décodé.
|
||||
func (dec *JsonDecoder) DecodeSlice() Result[Slice] {
|
||||
var sl Slice
|
||||
if err := dec.d.Decode(&sl); err != nil {
|
||||
return Err[Slice](err)
|
||||
}
|
||||
return Ok(sl)
|
||||
}
|
||||
|
||||
// Decode décode une entrée brute et la fusionne avec l’objet Json.
|
||||
func (o Json) Decode(r io.Reader) Result[Json] {
|
||||
dec := NewJsonDecoder(r)
|
||||
result := dec.Decode()
|
||||
|
||||
if o2, ok := result.Ok(); ok {
|
||||
o = o.Fusion(o2)
|
||||
return Ok(o)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Decode décode une entrée brute et la fusionne avec le tableau de Json.
|
||||
func (sl *Slice) Decode(r io.Reader) Result[Slice] {
|
||||
dec := NewJsonDecoder(r)
|
||||
result := dec.DecodeSlice()
|
||||
|
||||
if sl2, ok := result.Ok(); ok {
|
||||
return Ok(sl.Add(sl2...))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// JsonEncoder permet de convertir un objet Json ou un tableau de Json au format brut.
|
||||
type JsonEncoder struct {
|
||||
e *j.Encoder
|
||||
}
|
||||
|
||||
// NewJsonEncoder fournit un encodeur pour la sortie fournie.
|
||||
func NewJsonEncoder(w io.Writer) *JsonEncoder { return &JsonEncoder{e: j.NewEncoder(w)} }
|
||||
|
||||
// Encode encode un objet Json.
|
||||
func (enc *JsonEncoder) Encode(o Json) error { return enc.e.Encode(o) }
|
||||
|
||||
// EncodeSlice encode un tableau Json.
|
||||
func (enc *JsonEncoder) EncodeSlice(sl Slice) error { return enc.e.Encode(sl) }
|
||||
|
||||
// SetEscapeHTML configure l’encodeur pour que les caractère HTML soient échappés si on est vrai.
|
||||
// Ainsi, les caractères &, <, et > sont transformés respectivement en \u0026, \u003c et \u003e
|
||||
// Cela permet d’éviter des problèmes de sécurité lorsque du html est embarqué dans du Json.
|
||||
func (enc *JsonEncoder) SetEscapeHTML(on bool) { enc.e.SetEscapeHTML(on) }
|
||||
|
||||
// SetIndent définit le préfixe et l’indentation du Json à la sortie.
|
||||
// Cela permet de rendre un Json dans un format lisible (ie. non minifié).
|
||||
func (enc *JsonEncoder) SetIndent(prefix, indent string) { enc.e.SetIndent(prefix, indent) }
|
||||
|
||||
// Encode minifie le Json dans la sortie donnée.
|
||||
func (o Json) Encode(w io.Writer) error {
|
||||
enc := NewJsonEncoder(w)
|
||||
return enc.Encode(o)
|
||||
}
|
||||
|
||||
// EncodeHuman encode le Json dans un format humainement lisible (indentation de deux espaces).
|
||||
func (o Json) EncodeHuman(w io.Writer) error {
|
||||
enc := NewJsonEncoder(w)
|
||||
enc.SetIndent("", " ")
|
||||
return enc.Encode(o)
|
||||
}
|
||||
|
||||
// Encode minifie le tableau de Json dans la sortie donnée.
|
||||
func (sl Slice) Encode(w io.Writer) error {
|
||||
enc := NewJsonEncoder(w)
|
||||
return enc.EncodeSlice(sl)
|
||||
}
|
||||
|
||||
// EncodeHuman encode le tableau de Json dans un format humainement lisible (indentation de deux espaces).
|
||||
func (sl Slice) EncodeHuman(w io.Writer) error {
|
||||
enc := NewJsonEncoder(w)
|
||||
enc.SetIndent("", " ")
|
||||
return enc.EncodeSlice(sl)
|
||||
}
|
|
@ -0,0 +1,548 @@
|
|||
package json
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"gitea.zaclys.com/bvaudour/gob/collection"
|
||||
"gitea.zaclys.com/bvaudour/gob/convert"
|
||||
. "gitea.zaclys.com/bvaudour/gob/option"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrFailedToParse = errors.New("failed to parse")
|
||||
)
|
||||
|
||||
func isSlice(c convert.Value) bool {
|
||||
return c.Is(convert.Slice)
|
||||
}
|
||||
|
||||
func isMap(c convert.Value) bool {
|
||||
return c.Is(convert.Map) && c.Type().Key().Is(convert.String)
|
||||
}
|
||||
|
||||
func isAssignableIn(v1, v2 convert.Value) bool {
|
||||
return v1.Type().AssignableTo(v2.TypeElem())
|
||||
}
|
||||
|
||||
func searchSlice(c convert.Value, k string) (out Option[convert.Value]) {
|
||||
i, err := strconv.Atoi(k)
|
||||
if err == nil && i >= 0 && i < c.Len() {
|
||||
out = Some(c.Index(i))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func searchMap(c convert.Value, k string) (out Option[convert.Value]) {
|
||||
return c.MapIndexIfExists(convert.ValueOf(k))
|
||||
}
|
||||
|
||||
func search(c convert.Value, k string) (v Option[convert.Value]) {
|
||||
if isSlice(c) {
|
||||
v = searchSlice(c, k)
|
||||
} else if isMap(c) {
|
||||
v = searchMap(c, k)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func searchAll(c any, keys []string) (v Option[any]) {
|
||||
vv := convert.ValueOf(c)
|
||||
var ok bool
|
||||
for _, k := range keys {
|
||||
if vv, ok = search(vv, k).Get(); !ok {
|
||||
return
|
||||
}
|
||||
}
|
||||
return Some(vv.Interface())
|
||||
}
|
||||
|
||||
func setSlice(c convert.Value, v any, k string) (out Option[convert.Value]) {
|
||||
i, err := strconv.Atoi(k)
|
||||
vv := convert.ValueOf(v)
|
||||
l := c.Len()
|
||||
if err != nil && i >= 0 && i <= l && isAssignableIn(vv, c) {
|
||||
if i < l {
|
||||
c.SetIndex(i, vv)
|
||||
out = Some(c)
|
||||
} else {
|
||||
out = Some(convert.Append(c, vv))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setMap(c convert.Value, v any, k string) (out Option[convert.Value]) {
|
||||
vv := convert.ValueOf(v)
|
||||
if isAssignableIn(vv, c) {
|
||||
c.SetMapIndex(convert.ValueOf(k), vv)
|
||||
out = Some(c)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func set(c convert.Value, v any, k string) (out Option[convert.Value]) {
|
||||
if isSlice(c) {
|
||||
return setSlice(c, v, k)
|
||||
} else if isMap(c) {
|
||||
return setMap(c, v, k)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setAll(c, v any, keys []string) (ok bool) {
|
||||
l := len(keys)
|
||||
k1, k2 := keys[l-2], keys[l-1]
|
||||
cc := convert.ValueOf(c)
|
||||
for _, k := range keys[:l-2] {
|
||||
if cc, ok = search(cc, k).Get(); !ok {
|
||||
return
|
||||
}
|
||||
}
|
||||
var ccl convert.Value
|
||||
if ccl, ok = search(cc, k1).Get(); ok {
|
||||
if ccl, ok = set(ccl, v, k2).Get(); ok {
|
||||
set(cc, ccl.Interface(), k1)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func delSlice(c convert.Value, k string) (out Option[convert.Value]) {
|
||||
i, err := strconv.Atoi(k)
|
||||
l := c.Len()
|
||||
if err != nil && i >= 0 && i < l {
|
||||
v := convert.SliceOf(c.Type(), l-1, l-1)
|
||||
convert.Copy(v.Slice(0, i), c.Slice(0, i))
|
||||
convert.Copy(v.Slice(i, l-1), c.Slice(i+1, l))
|
||||
return Some(v)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func delMap(c convert.Value, k string) (out Option[convert.Value]) {
|
||||
kk := convert.ValueOf(k)
|
||||
if ok := c.MapIndexIfExists(kk).IsDefined(); ok {
|
||||
c.DelMapIndex(kk)
|
||||
out = Some(c)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func del(c convert.Value, k string) (out Option[convert.Value]) {
|
||||
if isSlice(c) {
|
||||
return delSlice(c, k)
|
||||
} else if isMap(c) {
|
||||
return delMap(c, k)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func delAll(c any, keys []string) (ok bool) {
|
||||
l := len(keys)
|
||||
k1, k2 := keys[l-2], keys[l-1]
|
||||
cc := convert.ValueOf(c)
|
||||
for _, k := range keys[:l-2] {
|
||||
if cc, ok = search(cc, k).Get(); !ok {
|
||||
return
|
||||
}
|
||||
}
|
||||
var ccl convert.Value
|
||||
if ccl, ok = search(cc, k1).Get(); ok {
|
||||
if ccl, ok = del(ccl, k2).Get(); ok {
|
||||
set(cc, ccl.Interface(), k1)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func flat(v convert.Value, prefix ...string) (keys [][]string, values []any) {
|
||||
if isSlice(v) {
|
||||
it, l := v.SliceRange(), len(prefix)
|
||||
for it.Next() {
|
||||
k := make([]string, l+1)
|
||||
copy(k[:l], prefix)
|
||||
k[l] = strconv.Itoa(it.Index())
|
||||
kk, vv := flat(it.Value(), k...)
|
||||
collection.Add(&keys, kk...)
|
||||
collection.Add(&values, vv...)
|
||||
}
|
||||
} else if isMap(v) {
|
||||
it, l := v.MapRange(), len(prefix)
|
||||
for it.Next() {
|
||||
k := make([]string, l+1)
|
||||
copy(k[:l], prefix)
|
||||
k[l] = it.Key().String()
|
||||
kk, vv := flat(it.Value(), k...)
|
||||
collection.Add(&keys, kk...)
|
||||
collection.Add(&values, vv...)
|
||||
}
|
||||
} else {
|
||||
collection.Add(&keys, prefix)
|
||||
collection.Add(&values, v.Interface())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func createRec(o Json, keys []string) Json {
|
||||
e := o
|
||||
for _, k := range keys {
|
||||
v, ok := o[k]
|
||||
if ok {
|
||||
if m, ok := v.(Json); ok {
|
||||
e = m
|
||||
continue
|
||||
}
|
||||
}
|
||||
m := make(Json)
|
||||
e[k] = m
|
||||
e = m
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
func unflat(v convert.Value, split SplitFunc) (out convert.Value) {
|
||||
if isSlice(v) {
|
||||
it := v.SliceRange()
|
||||
for it.Next() {
|
||||
i, e := it.Index(), unflat(it.Value(), split)
|
||||
v.SetIndex(i, e)
|
||||
}
|
||||
return v
|
||||
} else if isMap(v) {
|
||||
result := make(Json)
|
||||
it := v.MapRange()
|
||||
for it.Next() {
|
||||
k, e := it.Key().String(), unflat(it.Value(), split)
|
||||
spl := SplitKey(k, split)
|
||||
if keys, ok := spl.Ok(); ok {
|
||||
result = createRec(result, keys)
|
||||
result.SetRecursive(e.Interface(), keys...)
|
||||
} else {
|
||||
result[k] = e.Interface()
|
||||
}
|
||||
}
|
||||
return convert.ValueOf(result)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func sSlice(c convert.Value) convert.Value {
|
||||
it := c.SliceRange()
|
||||
for it.Next() {
|
||||
i, v := it.Index(), slicify(it.Value())
|
||||
c.SetIndex(i, v)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func canSlice(c convert.Value) (out Option[map[int]string]) {
|
||||
if !isMap(c) {
|
||||
return
|
||||
}
|
||||
var keys []int
|
||||
skeys := make(map[int]string)
|
||||
it := c.MapRange()
|
||||
for it.Next() {
|
||||
k := it.Key().String()
|
||||
i, err := strconv.Atoi(k)
|
||||
if err != nil || i < 0 {
|
||||
return
|
||||
}
|
||||
collection.Add(&keys, i)
|
||||
skeys[i] = k
|
||||
}
|
||||
sort.Ints(keys)
|
||||
for i, j := range keys {
|
||||
if i != j {
|
||||
return
|
||||
}
|
||||
}
|
||||
return Some(skeys)
|
||||
}
|
||||
|
||||
func sMap(c convert.Value) convert.Value {
|
||||
if keys, ok := canSlice(c).Get(); ok {
|
||||
sl := convert.SliceOf(convert.SliceTypeOf(c.TypeElem()), len(keys), len(keys))
|
||||
it := sl.SliceRange()
|
||||
for it.Next() {
|
||||
i := it.Index()
|
||||
k := keys[i]
|
||||
v := slicify(c.MapIndex(convert.ValueOf(k)))
|
||||
sl.SetIndex(i, v)
|
||||
}
|
||||
return sl
|
||||
}
|
||||
it := c.MapRange()
|
||||
for it.Next() {
|
||||
k, v := it.Key(), slicify(it.Value())
|
||||
c.SetMapIndex(k, v)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func slicify(v convert.Value) convert.Value {
|
||||
if isSlice(v) {
|
||||
return sSlice(v)
|
||||
} else if isMap(v) {
|
||||
return sMap(v)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// Json représente un simple objet JSON.
|
||||
type Json map[string]any
|
||||
|
||||
// SplitFunc est une fonction qui découpe une clé en sous-clés.
|
||||
//
|
||||
// Paramètres :
|
||||
// - key : la clé à découper
|
||||
//
|
||||
// Sortie :
|
||||
// - current : la sous-clé parsée
|
||||
// - next : la partie de clé qui reste à parser
|
||||
// - err : une erreur retournée lors du parsage ou io.EOF si le parsage est terminé
|
||||
type SplitFunc func(key string) (current, next string, err error)
|
||||
|
||||
// MergeFunc est la transformée inverse de SplitFunc.
|
||||
type MergeFunc func(keys []string) (key string)
|
||||
|
||||
// SplitDot découpe une clé qui est sous la forme a.b.c…
|
||||
func SplitDot(key string) (current, next string, err error) {
|
||||
i := strings.Index(key, ".")
|
||||
if i < 0 {
|
||||
current, err = key, io.EOF
|
||||
} else {
|
||||
current, next = key[:i], key[i+1:]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// SplitBracket découpe une clé qui est sous la forme a[b][c]…
|
||||
func SplitBracket(key string) (current, next string, err error) {
|
||||
i0 := strings.Index(key, "[")
|
||||
i1 := strings.Index(key, "]")
|
||||
if i0 < 0 {
|
||||
if i1 >= 0 {
|
||||
err = ErrFailedToParse
|
||||
} else {
|
||||
current, err = key, io.EOF
|
||||
}
|
||||
} else if i0 >= i1 || (len(key) > i1+1 && key[i1+1] != '[') {
|
||||
err = ErrFailedToParse
|
||||
} else {
|
||||
current, next = key[:i0], key[i0+1:i1]+key[i1:]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// SplitKey découpe la clé selon la fonction de découpage fournie.
|
||||
func SplitKey(key string, split SplitFunc) (out Result[[]string]) {
|
||||
current, next := key, ""
|
||||
var err error
|
||||
var keys []string
|
||||
for current, next, err = split(current); err == nil; current, next, err = split(current) {
|
||||
collection.Add(&keys, current)
|
||||
current = next
|
||||
}
|
||||
|
||||
if err == io.EOF {
|
||||
return Ok(keys)
|
||||
}
|
||||
return Err[[]string](err)
|
||||
}
|
||||
|
||||
// MergeDot crée une clé sous la forme a.b.c…
|
||||
func MergeDot(keys []string) string {
|
||||
return strings.Join(keys, ".")
|
||||
}
|
||||
|
||||
// MergeBracket crée une clé sous la forme a[b][c]…
|
||||
func MergeBracket(keys []string) (key string) {
|
||||
if len(keys) == 0 {
|
||||
return
|
||||
}
|
||||
key, keys = keys[0], keys[1:]
|
||||
for _, k := range keys {
|
||||
key += "[" + k + "]"
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (o Json) get(keys ...string) (v Option[any]) {
|
||||
switch len(keys) {
|
||||
case 0:
|
||||
v = Some[any](o)
|
||||
case 1:
|
||||
v = Some[any](o[keys[0]])
|
||||
default:
|
||||
v = searchAll(o, keys)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Get retourne la valeur de la clé.
|
||||
// Si une fonction de découpage est fournie, la clé est recherchée de façon récursive après découpage.
|
||||
func (o Json) Get(key string, split ...SplitFunc) (value Option[any]) {
|
||||
if len(split) == 0 {
|
||||
return o.get(key)
|
||||
}
|
||||
if keys, ok := SplitKey(key, split[0]).Ok(); ok {
|
||||
return o.get(keys...)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// GetRecursive retourne la valeur de la clé (de façon récursive).
|
||||
func (o Json) GetRecursive(keys ...string) (value Option[any]) {
|
||||
return o.get(keys...)
|
||||
}
|
||||
|
||||
// Parse injecte la valeur associée à la clé dans out, si possible,
|
||||
// et retourne vrai si l’injection a été possible.
|
||||
// out doit être un pointeur.
|
||||
// Si split est fourni la clé est découpée.
|
||||
func (o Json) Parse(out any, key string, split ...SplitFunc) (ok bool) {
|
||||
var v any
|
||||
if v, ok = o.Get(key, split...).Get(); ok {
|
||||
ok = convert.Convert(out, v)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ParseRecurive agit comme Parse, récursivement.
|
||||
func (o Json) ParseRecursive(out any, keys ...string) (ok bool) {
|
||||
var v any
|
||||
if v, ok = o.GetRecursive(keys...).Get(); ok {
|
||||
ok = convert.Convert(out, v)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// SafeParse agit comme Parse mais la valeur trouvée doit être compatible avec le type de out.
|
||||
func (o Json) SafeParse(out any, key string, split ...SplitFunc) (ok bool) {
|
||||
var v any
|
||||
if v, ok = o.Get(key, split...).Get(); ok {
|
||||
ok = convert.Convert(out, v, true)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// SafeParseRecursive agit comme SafeParse, récursivement.
|
||||
func (o Json) SafeParseRecursive(out any, keys ...string) (ok bool) {
|
||||
var v any
|
||||
if v, ok = o.GetRecursive(keys...).Get(); ok {
|
||||
ok = convert.Convert(out, v, true)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (o Json) set(value any, keys ...string) (ok bool) {
|
||||
l := len(keys)
|
||||
switch l {
|
||||
case 0:
|
||||
case 1:
|
||||
o[keys[0]], ok = value, true
|
||||
default:
|
||||
ok = setAll(o, value, keys)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Set insère la valeur dans l’objet et retourne vrai si la valeur a été insérée.
|
||||
// Si la fonction de découpage est fournie, la clé est découpée.
|
||||
func (o Json) Set(value any, key string, split ...SplitFunc) (ok bool) {
|
||||
if len(split) == 0 {
|
||||
return o.set(value, key)
|
||||
}
|
||||
var keys []string
|
||||
if keys, ok = SplitKey(key, split[0]).Ok(); ok {
|
||||
return o.set(value, keys...)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SetRecursive insère la valeur de façon récursive dans l’objet et retourne vrai si elle existe.
|
||||
func (o Json) SetRecursive(value any, keys ...string) (ok bool) {
|
||||
return o.set(value, keys...)
|
||||
}
|
||||
|
||||
func (o Json) del(keys ...string) (ok bool) {
|
||||
l := len(keys)
|
||||
switch l {
|
||||
case 0:
|
||||
case 1:
|
||||
if ok = o.Get(keys[0]).IsDefined(); ok {
|
||||
delete(o, keys[0])
|
||||
}
|
||||
default:
|
||||
ok = delAll(o, keys)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Si la fonction de découpage est fournie, la clé est découpée.
|
||||
func (o Json) Del(key string, split ...SplitFunc) (ok bool) {
|
||||
if len(split) == 0 {
|
||||
return o.del(key)
|
||||
}
|
||||
var keys []string
|
||||
if keys, ok = SplitKey(key, split[0]).Ok(); ok {
|
||||
return o.del(keys...)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DelRecursive supprimer la clé de façon récursive dans l’objet et retourne vrai si elle existe.
|
||||
func (o Json) DelRecursive(keys ...string) (ok bool) {
|
||||
return o.del(keys...)
|
||||
}
|
||||
|
||||
// Clone effectue une copie profonde de l’objet.
|
||||
func (o Json) Clone(deep ...bool) Json {
|
||||
c := make(Json)
|
||||
for k, v := range o {
|
||||
c.Set(convert.Clone(v, deep...), k)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// Fusion ajoute toutes les entrées de o2 dans o1 et retourne o1
|
||||
func (o1 Json) Fusion(o2 Json) Json {
|
||||
for k, v := range o2 {
|
||||
o1[k] = v
|
||||
}
|
||||
return o1
|
||||
}
|
||||
|
||||
// Flat aplatit la structure du Json.
|
||||
func (o Json) Flat(merge MergeFunc) (out Json) {
|
||||
keys, values := flat(convert.ValueOf(o))
|
||||
out = make(Json)
|
||||
for i, k := range keys {
|
||||
out[merge(k)] = values[i]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Unflat est l’opération inverse de Flat.
|
||||
func (o Json) Unflat(split SplitFunc) (out Json) {
|
||||
vo := unflat(convert.ValueOf(o), split)
|
||||
out = vo.Interface().(Json)
|
||||
|
||||
for k, v := range out {
|
||||
out[k] = slicify(convert.ValueOf(v))
|
||||
}
|
||||
return
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package json
|
||||
|
||||
// Slice est un tableau d’objets JSON.
|
||||
type Slice []Json
|
||||
|
||||
func (sl *Slice) Add(objects ...Json) Slice {
|
||||
*sl = append(*sl, objects...)
|
||||
return *sl
|
||||
}
|
||||
|
||||
func (sl *Slice) Insert(objects ...Json) Slice {
|
||||
*sl = append(objects, (*sl)...)
|
||||
return *sl
|
||||
}
|
Loading…
Reference in New Issue