2

Is it possible to modify json serialization and deserialization so that a struct like this:

type Foo struct {
   A int64
   B uint64
   // and other stuff with int64 and uint64 and there's a lot of struct that are like this
}

x := Foo{A: 1234567890987654, B: 987654321012345678}
byt, err := json.Marshal(x)
fmt.Println(err)
fmt.Println(string(byt))

//                                 9223372036854775808      9223372036854775808
err = json.Unmarshal([]byte(`{"A":"12345678901234567", "B":"98765432101234567"}`), &x)
// ^ must be quoted since javascript can't represent those values properly (2^53)
// ^ json: cannot unmarshal string into Go struct field Foo.A of type int64
fmt.Println(err)
fmt.Printf("%#v\n", x)
// main.Foo{A:1234567890987654, B:0xdb4da5f44d20b4e}

https://play.golang.org/p/dHN-FcJ7p-N

So that It could receive json string but parsed as int64/uint64, and can deserialize int64/uint64 as json string without have to modify the struct or struct tag at all

3
  • "without have to modify the struct or struct tag at all" no. The struct and struct tags are what determine how it is marshaled and unmarshaled. Commented Aug 19, 2021 at 18:15
  • You could however write a custom unmarshaling method with an anonymous struct and parse the strings to int64/uint64 locally. Commented Aug 19, 2021 at 18:31
  • 1
    Javascript often outputs inconsistent data as json. A more catch-all approach would be to unmarshall in interface{} type and then use a type switch... if it is detected as string you cast it to string and then use strings.ParseInt() if it isn't then you can take another route. Commented Aug 20, 2021 at 1:33

1 Answer 1

6

Use the string option in the JSON struct tag. It's designed exactly for this use case:

The "string" option signals that a field is stored as JSON inside a JSON-encoded string. It applies only to fields of string, floating point, integer, or boolean types. This extra level of encoding is sometimes used when communicating with JavaScript programs

type Foo struct {
    A int64  `json:"A,string"`
    B uint64 `json:"B,string"`
}

func main() {
    x := &Foo{}
    _ = json.Unmarshal([]byte(`{"A":"12345678901234567", "B":"98765432101234567"}`), x)

    fmt.Println(x) // &{12345678901234567 98765432101234567}

    b, _ := json.Marshal(x)

    fmt.Println(string(b)) // {"A":"12345678901234567","B":"98765432101234567"}
}

Playground: https://play.golang.org/p/IfpcYOlcKMo

If you can't modify existing struct tags (but your example has none), then you have to reinvent the implementation of this string tag option in a custom UnmarshalJSON and MarshalJSON methods.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for teaching me something new. This answer is simple and effective
ah too bad with string tag cannot parse plain integer play.golang.org/p/LIyt9ZdHPeg
@Kokizzu I think that is expected. With these standard tags, you either deserialize JSON strings to integers or JSON numbers to integers. If you need a deserializer that does both, you have to implement it yourself

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.