Cast json that contains interface in properly struct

眉间皱痕 提交于 2020-01-06 05:36:14

问题


i'm unable to cast the following json in a struct in Golang, received from Kraken API:

{
    "error": [],
    "result": {
        "LINKUSD": {
            "asks": [
                ["2.049720", "183.556", 1576323009],
                ["2.049750", "555.125", 1576323009],
                ["2.049760", "393.580", 1576323008],
                ["2.049980", "206.514", 1576322995]
            ],
            "bids": [
                ["2.043800", "20.691", 1576322350],
                ["2.039080", "755.396", 1576323007],
                ["2.036960", "214.621", 1576323006],
                ["2.036930", "700.792", 1576322987]
            ]
        }
    }
}

Using json-to-go, he gives me the following struct:

type AutoGenerated struct {
    Error  []interface{} `json:"error"`
    Result struct {
        LINKUSD struct {
            Asks [][]interface{} `json:"asks"`
            Bids [][]interface{} `json:"bids"`
        } `json:"LINKUSD"`
    } `json:"result"`
}

Obviously, i can't hardocode the LINKUSD cause it will change for every currency pairs.

I've created two structure for accomplish the task, but i'm not able to cast the result in the struct.

type BitfinexOrderBook struct {
    Pair string          `json:"pair"`
    Asks []BitfinexOrder `json:"asks"`
    Bids []BitfinexOrder `json:"bids"`
}

type BitfinexOrder struct {
    Price     string
    Volume    string
    Timestamp time.Time
}

My first attempt was to use reflection. Reading the JSON data posted above, i'm able to retrieve an interface that contains the asks and bids list.

// Just used as a divisor
const div string = " ---------------------------------"
func test(data []byte) error {
    var err error

    // Creating the maps for the JSON data
    m := map[string]interface{}{}

    // Parsing/Unmarshalling the json readed from the file
    err = json.Unmarshal(data, &m)

    if err != nil {
        log.Println("Error unmarshalling data: " + err.Error())
        return err
    }

    // Extract the "result" only
    a := reflect.ValueOf(m["result"])

    if a.Kind() == reflect.Map {
        key := a.MapKeys()[0] // Extract the key -> LINKUSD
        log.Println("KEY: ", key, div)
        strct := a.MapIndex(key) // Extract the value -> asks and bids array
        log.Println("MAP: ", strct, div)
        m, _ := strct.Interface().(map[string]interface{})
        log.Println("Asks: ", m["asks"], div) // This will print [[value, value, value] ...] related to asks
        log.Println("Bids: ", m["bids"], div) // This will print [[value, value, value] ...] related to bids

        // Parse the interface into a []byte
        asks_data, err := json.Marshal(m["asks"])
        log.Println("OK: ", err, div)
        log.Println("ASKS: ", string(asks_data), div)
        // Tried without array to (datastructure.BitfinexOrder)
        var asks []datastructure.BitfinexOrder
        err = json.Unmarshal(asks_data, &asks)
        log.Println("ERROR: ", err, div)
        log.Println("UNMARSHAL: ", asks, div)

    }
    return errors.New("UNABLE_PARSE_VALUE")
}

The two print below m, _ := strct.Interface().(map[string]interface{}) will show the following similar data that i'm not able to cast due to the fact that are of interface type:

[[2.049720 183.556 1.576323009e+09] [2.049750 555.125 1.576323009e+09] [2.049760 393.580 1.576323008e+09] [2.049980 206.514 1.576322995e+09]]

But i'm not able to unmarshal the data.

So i've tried with a different function provided by @chmike:

// UnmarshalJSON decode a BifinexOrder.
func UnmarshalJSON(data []byte) (datastructure.BitfinexOrder, error) {
    var packedData []json.Number

    var order datastructure.BitfinexOrder
    err := json.Unmarshal(data, &packedData)
    if err != nil {
        return order, err
    }
    order.Price = packedData[0].String()
    order.Volume = packedData[1].String()
    t, err := packedData[2].Int64()
    if err != nil {
        return order, err
    }
    order.Timestamp = time.Unix(t, 0)
    return order, nil
}

But i receive the following error:

json: cannot unmarshal array into Go value of type json.Number

Some tips?

NOTE: My previous questions was closed as a duplicate of "Unmarshal 2 different structs in a slice". But, if you read the questions, i'm not dealing with two different structs... I'm dealing with a json that contains a key that i does not know at prior. I'm not able to marshal BitfinexOrder too


回答1:


Solution found from @mkopriva and @chmike:

Thank you guys!

package main

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

var data = []byte(`{
    "error": [],
    "result": {
        "LINKUSD": {
            "asks": [
                ["2.049720", "183.556", 1576323009],
                ["2.049750", "555.125", 1576323009],
                ["2.049760", "393.580", 1576323008],
                ["2.049980", "206.514", 1576322995]
            ],
            "bids": [
                ["2.043800", "20.691", 1576322350],
                ["2.039080", "755.396", 1576323007],
                ["2.036960", "214.621", 1576323006],
                ["2.036930", "700.792", 1576322987]
            ]
        }
    }
}`)

type Response struct {
    Error  []interface{}          `json:"error"`
    Result map[string]Order `json:"result"`
}

type Order struct {
    Asks []BitfinexOrder `json:"asks"`
    Bids []BitfinexOrder `json:"bids"`
}

type BitfinexOrder struct {
    Price     string
    Volume    string
    Timestamp time.Time
}

// UnmarshalJSON decode a BifinexOrder.
func (b *BitfinexOrder) UnmarshalJSON(data []byte) error {
    var packedData []json.Number
    err := json.Unmarshal(data, &packedData)
    if err != nil {
        return err
    }
    b.Price = packedData[0].String()
    b.Volume = packedData[1].String()
    t, err := packedData[2].Int64()
    if err != nil {
        return err
    }
    b.Timestamp = time.Unix(t, 0)
    return nil
}

func main() {
    res := &Response{}
    if err := json.Unmarshal(data, res); err != nil {
        panic(err)
    }

    for key, value := range res.Result {
        fmt.Println(key)
        for i, ask := range value.Asks {
            fmt.Printf("Asks[%d] = %#v\n", i, ask)
        }
        for i, bid := range value.Bids {
            fmt.Printf("Bids[%d] = %#v\n", i, bid)
        }
    }
}


来源:https://stackoverflow.com/questions/59338416/cast-json-that-contains-interface-in-properly-struct

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!