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