Home > OS >  Golang: Unmarshal mapset
Golang: Unmarshal mapset

Time:08-15

I'm trying to Unmarshal mapset in user-defined struct, but I keep getting this error:

json: cannot unmarshal array into Go struct field A.b of type mapset.Set[int] {<nil>}

Sample code:

package main

import (
    "encoding/json"
    "fmt"

    mapset "github.com/deckarep/golang-set/v2"
)

type A struct {
    B mapset.Set[int] `json:"b"`
}

func main() {
    a := A{mapset.NewSet(1, 2, 3)}
    r, err := json.Marshal(a)
    fmt.Println(r, err)

    var b A
    err = json.Unmarshal(r, &b)
    fmt.Println(b, err)
}

I tried to unmarshal not a type struct, but mapset itself, but the result is the same one.

Am I doing something wrong or it is some iternal package issue?

CodePudding user response:

You can wrap this mapset.Set[int] type into a custom struct type and write a json.Unmarshaler implementation for it. I've never used this library, but here is an example of how can you do that, probably not the most elegant solution but it seems to work fine (it's a modified version of the code in your question). Comparison also works using either reflect.DeepEqual() or a set's Equal() method.

package main

import (
    "encoding/json"
    "fmt"
    "log"
    "reflect"

    mapset "github.com/deckarep/golang-set/v2"
)

type IntSet struct {
    Values mapset.Set[int] `json:"values"`
}

func (s *IntSet) UnmarshalJSON(data []byte) error {
    // Anonymous struct for the unmarshalling purposes.
    v := struct {
        Values []int `json:"values"`
    }{}

    // Unmarshal bytes into a custom struct that
    // contains int slice as the only value.
    if err := json.Unmarshal(data, &v); err != nil {
        return err
    }

    // If there is no set inside a struct, create
    // a new set with a single '0' value.
    if s.Values == nil {
        s.Values = mapset.NewSet(0)
    }

    // Empty the set.
    s.Values.Clear()

    // Add all the values from the unmarshalled
    // bytes to the struct set.
    for _, value := range v.Values {
        s.Values.Add(value)
    }

    return nil
}

func main() {
    a := IntSet{Values: mapset.NewSet(1, 2, 3)}
    r, err := json.Marshal(a)
    fmt.Println(r, err)

    var b IntSet
    err = json.Unmarshal(r, &b)
    fmt.Println(b, err)

    // Compare a and b using [reflect.DeepEqual].
    if !reflect.DeepEqual(a, b) {
        log.Fatalln("a and b are not equal")
    }

    // Compare a and v values by using any
    // of the sets' Equal() method.
    if !a.Values.Equal(b.Values) || !b.Values.Equal(a.Values) {
        log.Fatalln("a and b values are not equal")
    }
}

Also if you intend to put the sets comparison in your final code, please note that using Equal() method on the two sets is much faster than using reflect.DeepEqual(). The first comparison example in the code block above is just for the testing purposes, you should generally avoid using reflection in a production code.

  • Related