francoispqt / gojay

high performance JSON encoder/decoder with stream API for Golang
MIT License
2.11k stars 112 forks source link

Enhancement Request: Support Slices and Arrays as builtin types #54

Closed veqryn closed 5 years ago

veqryn commented 6 years ago

Right now, having to create custom types for every single slice or array in your structs is pretty annoying. Additionally, it can be annoying to have to cast it back to the original type, though golang does a good job of not requiring this most of the time.

Would it be possible to support slices and arrays natively, perhaps by forcing the logic into the UnmarshalJSONObject/MarshalJSONObject functions?

For example, instead of:

type Record struct {
    Loc Location   `json:"loc,omitempty"`
}

type Location []float64

func (l Location) UnmarshalJSONArray(dec *gojay.Decoder) error {
    fl := 0.0
    if err := dec.Float64(&fl); err != nil {
        return err
    }
    l = append(l, fl)
    return nil
}

func (p *Record) UnmarshalJSONObject(dec *gojay.Decoder, key string) error {
    switch key {
    case "loc":
        return dec.Array(&p.Loc)
    }
    return nil
}

Maybe something like this (rough pseudocode):

type Record struct {
    Loc []float64  `json:"loc,omitempty"`
}

func (p *Record) UnmarshalJSONObject(dec *gojay.Decoder, key string) error {
    switch key {
    case "loc":
        return dec.SliceFloat64(&p.Loc)
    }
    return nil
}

or

func (p *Record) UnmarshalJSONObject(dec *gojay.Decoder, key string) error {
    switch key {
    case "loc":
        return dec.ArrayFunc(&p.Loc, func(dec *gojay.Decoder) error {
            fl := 0.0
            if err := dec.Float64(&fl); err != nil {
                return err
            }
            l = append(l, fl)
            return nil
        })
    }
    return nil
}
francoispqt commented 6 years ago

Sure this, seems like a good idea. We are preparing a release for new types (time.Time and nullable types from the sql package), will squeeze a few array/slice types there.

crosslogic commented 6 years ago

Something like this would be great:

type InformeDiario struct {
    Conceptos    []models.Concepto
    Cuentas      []models.Cuenta
    Columnas     []string
    Stocks       []idNombre
}

func (i InformeDiario) MarshalJSONObject(e *gojay.Encoder) {

    arr := e.NewArray()
    for _, v := range i.Conceptos {
        item = e.NewArrayItem()
        item.AddStringKey("name", v.Name)
        item.AddStringKey("number", v.Number)
        arr.Add(item)
    }

    e.AddArrayKey("conceptos", arr)
}
francoispqt commented 6 years ago

There is something that already allows to do this:

func (i InformeDiario) MarshalJSONObject(e *gojay.Encoder) {
    e.AddArrayKey("conceptos", gojay.EncodeArrayFunc(func(enc *gojay.Encoder){
              for _, v := range i.Conceptos {
             enc.AddObject(gojay.EncodeObjectFunc(func(enc *gojay.Encoder) {
                          enc.AddStringKey("name", v.Name)
                  enc.AddStringKey("number", v.Number)
                     }))
           }
        })
}

Maybe will add a syntax similar to yours in the future but for now I will just add some basic slice types like:

enc.AddSliceStrKey("someSlice", v)
enc.AddSliceIntKey("someSlice", v)
enc.AddSliceBoolKey("someSlice", v)
enc.AddSliceObjectKey("someSlice", v)
enc.AddSliceSliceKey("someSlice", v)
francoispqt commented 5 years ago

Just added

dec.AddSliceString(*[]string)
dec.AddSliceInt(*[]int)
dec.AddSliceFloat64(*[]float64)
dec.AddSliceBool(*[]bool)

And there encoding counterparts. I'm closing this one.