JSON Tags - doesn't work

Suppose, we have a struct:

type DailyExchangeDetails struct {
	Date     string `json:"date"`
	Currency map[string]float64 `json:"btc"`
}

But the JSON attribute, field name can differ - it could be “btc”, but also “eur” or “pln”. How to create a general struct with a json tag that manages to cover that? The problem is that without proper tags i.e: `json:"pln"` the encoding of JSON doesn’t work properly. Moreover, if I omit the JSON tags, it still doesn’t work properly.

This is a code for encoding:

func fromJSON[T any](res *http.Response, target *T) error {
	//
	buf := new(bytes.Buffer)
	buf.ReadFrom(res.Body)

	err := json.Unmarshal(buf.Bytes(), target)

	if err != nil {
		log.Print(err)
		log.Fatalf("Error unmarshal")
	}
	return err
}

This is a slice of JSON file:

{
	"date": "2024-05-15",
	"btc": {
		"$myro": 388814.57395719,
		"$wen": 399391074.4242139,
		"00": 855345.08008341,
		"0x0": 205498.96296352,
		"1000sats": 237075658.30367133,
		"10set": 62400.24253448,
		"1inch": 173185.53022438,
		"aave": 765.26611448,
		"abt": 15467.94403195
         }
}

I noticed that the problem is with a map. This is a struct returned from unmarshall:
{2024-05-16 map[]} . As you can see, the map is somehow empty.

Hello there. How do you pass your T into the function? I wrote a simple test and everything has been parsed as expected.

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"log"
	"os"
)

type DailyExchangeDetails struct {
	Date     string             `json:"date"`
	Currency map[string]float64 `json:"btc"`
}

func main() {
	data, err := os.ReadFile("test.json")
	if err != nil {
		log.Println(err)
		return
	}

	var t DailyExchangeDetails

	if err := fromJSON(data, &t); err != nil {
		log.Println(err)
		return
	}

	fmt.Printf("%#v\n", t)
}

func fromJSON[T any](data []byte, target T) error {
	buf := bytes.NewBuffer(data)

	return json.Unmarshal(buf.Bytes(), &target)
}

Output:

% go run main.go
main.DailyExchangeDetails{Date:"2024-05-15", Currency:map[string]float64{"$myro":388814.57395719, "$wen":3.993910744242139e+08, "00":855345.08008341, "0x0":205498.96296352, "1000sats":2.3707565830367133e+08, "10set":62400.24253448, "1inch":173185.53022438, "aave":765.26611448, "abt":15467.94403195}}

UPD: or you just meant you cannot parse it with over tags?

If you add JSON tags it works properly. Do it without them and it would not. As I said, json field name can differ, but the structure is the same. We have string and map.

{
	"date": "2024-05-15",
	"pln": {
		"$myro": 388814.57395719,
		"$wen": 399391074.4242139,
		"00": 855345.08008341,
		"0x0": 205498.96296352,
		"1000sats": 237075658.30367133,
		"10set": 62400.24253448,
		"1inch": 173185.53022438,
		"aave": 765.26611448,
		"abt": 15467.94403195
         }
}

If you changed currency to PLN, it would’t work.

Yeah, I see now. In this case you can browse for third party json libs which support multiple tags definitions. With standard library the solutions will be to write your own unmarshal or decode into map[string]any instead of a struct.

Hmm, but why wouldn’t it work without tags? As I know tags are not necessary. Really strange.

If you don’t use explicit tags on struct fields, then json assumes that a field name equals your struct field name. In your example, if you remove tags from the struct, it expects date and currency as fields in json file.

2 Likes

Ok. Thanks for your time and explanation :slight_smile:

1 Like

Thanks for helping me out as well, I was confused before. You made my :slight_smile: