104

I need to decode a JSON string with the float number like:

{"name":"Galaxy Nexus", "price":"3460.00"}

I use the Golang code below:

package main

import (
    "encoding/json"
    "fmt"
)

type Product struct {
    Name  string
    Price float64
}

func main() {
    s := `{"name":"Galaxy Nexus", "price":"3460.00"}`
    var pro Product
    err := json.Unmarshal([]byte(s), &pro)
    if err == nil {
        fmt.Printf("%+v\n", pro)
    } else {
        fmt.Println(err)
        fmt.Printf("%+v\n", pro)
    }
}

When I run it, get the result:

json: cannot unmarshal string into Go value of type float64
{Name:Galaxy Nexus Price:0}

I want to know how to decode the JSON string with type convert.

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
yanunon
  • 1,151
  • 2
  • 8
  • 8

4 Answers4

221

The answer is considerably less complicated. Just add tell the JSON interpeter it's a string encoded float64 with ,string (note that I only changed the Price definition):

package main

import (
    "encoding/json"
    "fmt"
)

type Product struct {
    Name  string
    Price float64 `json:",string"`
}

func main() {
    s := `{"name":"Galaxy Nexus", "price":"3460.00"}`
    var pro Product
    err := json.Unmarshal([]byte(s), &pro)
    if err == nil {
        fmt.Printf("%+v\n", pro)
    } else {
        fmt.Println(err)
        fmt.Printf("%+v\n", pro)
    }
}
Dustin
  • 89,080
  • 21
  • 111
  • 133
  • Thank you! I think this is the best solution for my problem. Could you tell me where is the official document about the usage of ",string"? – yanunon Mar 06 '12 at 02:35
  • 4
    Note that the leading comma in front of `json:",string"` is necessary - it won't work without it. – Darrrrrren Nov 25 '14 at 15:18
  • 2
    @dustin any idea how to do that other way around? Convert json number to go string? e.g. `"N": 1234` to `N: "1234"`? – Kamil Dziedzic Feb 12 '16 at 16:32
  • That's really awesome. Except it doesn't work on slices. Does anyone know a way how to do this for []int ? Simply adding the ",string" flag produces: "cannot unmarshal string into go value of int" (http://play.golang.org/p/aFWSH4lUxv) – Dalibor Filus Mar 03 '16 at 10:58
  • @KamilDziedzic did you get a solution of your query? – maverick May 13 '20 at 16:34
  • 1
    Official document: https://golang.org/pkg/encoding/json/#Marshal `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` – dao leno May 21 '20 at 03:05
  • 3
    Does anybody know how to accept both number and string? – aolivera Aug 08 '21 at 17:59
  • When serializing a float with 0 decimals, eg 123.00, it is serialized as `"field": "123"` without any decimals. Is there a way for me to always include 2 decimals? – Gustav Sep 20 '22 at 15:31
  • sounds interesting, do you think it works from int to string ? I tried this https://go.dev/play/p/wLNA1Zu0CAU, but it returns **json: cannot unmarshal number into Go struct field Product.Price of type string** – Yuseferi Jun 05 '23 at 16:20
21

Just letting you know that you can do this without Unmarshal and use json.decode. Here is Go Playground

package main

import (
    "encoding/json"
    "fmt"
    "strings"
)

type Product struct {
    Name  string `json:"name"`
    Price float64 `json:"price,string"`
}

func main() {
    s := `{"name":"Galaxy Nexus","price":"3460.00"}`
    var pro Product
    err := json.NewDecoder(strings.NewReader(s)).Decode(&pro)
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println(pro)
}
Salvador Dali
  • 214,103
  • 147
  • 703
  • 753
6

Avoid converting a string to []byte: b := []byte(s). It allocates a new memory space and copy the whole the content into it.

strings.NewReader interface is better. Below is the code from godoc:

package main

import (
    "encoding/json"
    "fmt"
    "io"
    "log"
    "strings"
)

func main() {
    const jsonStream = `
    {"Name": "Ed", "Text": "Knock knock."}
    {"Name": "Sam", "Text": "Who's there?"}
    {"Name": "Ed", "Text": "Go fmt."}
    {"Name": "Sam", "Text": "Go fmt who?"}
    {"Name": "Ed", "Text": "Go fmt yourself!"}
`
    type Message struct {
        Name, Text string
    }
    dec := json.NewDecoder(strings.NewReader(jsonStream))
    for {
        var m Message
        if err := dec.Decode(&m); err == io.EOF {
            break
        } else if err != nil {
            log.Fatal(err)
        }
        fmt.Printf("%s: %s\n", m.Name, m.Text)
    }
}
g10guang
  • 4,647
  • 3
  • 28
  • 22
  • Your answer makes a great point that I quoted. Did I add to it? See https://stackoverflow.com/a/62740786/12817546 –  Jul 07 '20 at 21:50
4

Passing a value in quotation marks make that look like string. Change "price":"3460.00" to "price":3460.00 and everything works fine.

If you can't drop the quotations marks you have to parse it by yourself, using strconv.ParseFloat:

package main

import (
    "encoding/json"
    "fmt"
    "strconv"
)

type Product struct {
    Name       string
    Price      string
    PriceFloat float64
}

func main() {
    s := `{"name":"Galaxy Nexus", "price":"3460.00"}`
    var pro Product
    err := json.Unmarshal([]byte(s), &pro)
    if err == nil {
        pro.PriceFloat, err = strconv.ParseFloat(pro.Price, 64)
        if err != nil { fmt.Println(err) }
        fmt.Printf("%+v\n", pro)
    } else {
        fmt.Println(err)
        fmt.Printf("%+v\n", pro)
    }
}
Mostafa
  • 26,886
  • 10
  • 57
  • 52
  • Is there a way that not changing the 'Product' struct and implement a decode function or interface to parse the string to float? – yanunon Feb 26 '12 at 13:26
  • 1
    @yanunon Yes, you can use a map of type `map[string]interface{}` for `Unmarshal`, and parse that into your struct. – Mostafa Feb 26 '12 at 14:11
  • @yanunon Or if you want **real** flexibility, you can write your own `Unmarshal`, which calls default `Unmarshal` with a `map[string]interface{}`, but uses `reflect` and `strconv` packages to do the parsing. – Mostafa Feb 26 '12 at 14:18