Golang에서 커스텀 방식으로 JSON을 언마샬링하는 방법
28941 단어 go
These are real world examples. (coinex exchange API)
먼저 다음과 같이 요청합니다.
raw_response, _ := http.Get("https://api.coinex.com/v1/market/list")
그리고 우리는 다음과 같은 json 객체를 얻을 것입니다:
{
"code": 0,
"data": [
"LTCBCH",
"ETHBCH",
"ZECBCH",
"DASHBCH"
],
"message": "Ok"
}
하지만 우리는 그것을 파싱해야 합니다.
파싱하는 방법은 다양하지만,
json 패키지의 NewDecoder 또는 Unmarshal 기능을 사용할 수 있습니다.
struct
또는 map[string]interface{}
로 디코딩할 수 있습니다.선호도에 따라 다르지만 이 경우 NewDecoder와 구조체 조합을 선호합니다.
따라서 다음과 같은 구조체를 만들어야 합니다.
type AllMarketList struct {
Code int `json:"code"`
Message string `json:"message"`
Data []string
}
또한 포함된 구조체를 가질 수 있습니다. 예를 들어 마지막 구조체를 두 개로 나눕니다.
type GeneralResponse struct {
Code int `json:"code"`
Message string `json:"message"`
}
type AllMarketList struct {
GeneralResponse
Data []string
}
그리고 차이가 없습니다.
마지막으로 NewDecoder를 사용하여 raw_response를 AllMarketList 구조체로 디코딩합니다.
var allMarketList AllMarketList
json.NewDecoder(raw_response.Body).Decode(&allMarketList)
완성된 코드
package main
import (
"encoding/json"
"fmt"
"net/http"
)
type AllMarketList struct {
Code int `json:"code"`
Message string `json:"message"`
Data []string
}
func main() {
raw_response, _ := http.Get("https://api.coinex.com/v1/market/list")
var allMarketList AllMarketList
if err := json.NewDecoder(raw_response.Body).Decode(&allMarketList); err != nil {
fmt.Println(err)
}
defer raw_response.Body.Close()
fmt.Printf("%+v\n", allMarketList)
}
예 2
다음과 같은 json이 있다고 생각하십시오.
{
"code": 0,
"data": {
"date": 1513865441609, # server time when returning
"ticker": {
"open": "10", # highest price
"last": "10.00", # latest price
"vol": "110" # 24H volume
}
},
"message" : "Ok"
}
디코딩 프로세스에서 몇 가지 사항을 개선할 예정입니다.
문제 1과 2는 디코딩하려는 구조체에 UnmarshalJSON 메서드를 구현하여 해결할 수 있습니다.
문제 3은 json 태그로 쉽게 해결됩니다. (아래 코드에서 언급했습니다)
최종 구조체는 다음과 같아야 합니다.
// Final struct
type SingleMarketStatistics struct {
Code int `json:"code"`
Message string `json:"message"`
Data TickerData
}
// Inner struct that we should implement to solve problem 2
type TickerData struct {
ServerTime CTime `json:"date"` // CTime is short for CustomTime
Open float64 `json:"open"`
Close float64 `json:"last"` // Different Attribute name and tag name
Volume float64 `json:"vol"` // Different Attribute name and tag name
}
// Custome time
// Inner struct that we should implement to solve problem 1
type CTime struct {
time.Time
}
맞춤형 시간 구현
func (t *CTime) UnmarshalJSON(data []byte) error {
// Ignore null, like in the main JSON package.
if string(data) == "null" || string(data) == `""` {
return nil
}
// Fractional seconds are handled implicitly by Parse.
i, err := strconv.ParseInt(string(data), 10, 64)
update := time.UnixMilli(i)
*t = CTime{update}
return err
}
그리고 더 이상 오류가 발생하지 않습니다!
이 메서드는 시간을 CTime으로 디코딩할 때마다 자동으로 사용됩니다(인터페이스 덕분에!)!
맞춤 데이터 구현
func (t *TickerData) UnmarshalJSON(data []byte) error {
if string(data) == "null" || string(data) == `""` {
return nil
}
// This is how this json really looks like.
var realTicker struct {
ServerTime CTime `json:"date"`
Ticker struct {
// tags also can be omitted when we're using UnmarshalJSON.
Open string `json:"open"`
Close string `json:"last"`
Volume string `json:"vol"`
} `json:"ticker"`
}
// Unmarshal the json into the realTicker struct.
if err := json.Unmarshal(data, &realTicker); err != nil {
return err
}
// Set the fields to the new struct,
// with any shape it has,
// or anyhow you want.
*t = TickerData{
ServerTime: realTicker.ServerTime,
Open: realTicker.Ticker.Open,
Close: realTicker.Ticker.Close,
Volume: realTicker.Ticker.Volume,
}
return nil
}
이제 이전처럼 NewDecoder를 사용하면 됩니다. 변경할 필요가 없습니다.
var singleMarketStatistics SingleMarketStatistics
json.NewDecoder(raw_response.Body).Decode(&allMarketList)
예 3
다음과 같은 JSON을 상상해보십시오.
{
"asks": [ // This is a array of asks
[ // This is a array of ONE ask
"10.00", // Price of ONE ask
"0.999", // Amount of ONE ask
]
],
"bids": [ // Same structure as asks
[
"10.00",
"1.000",
]
]
}
명확하게 알 수 있듯이 비전문적인 방식으로 "매수"를
[][]string
로 디코딩하고 첫 번째 매도 가격asks[0][0]
및 금액asks[0][1]
에 액세스해야 합니다.0이 가격이고 1이 금액이라는 것을 누가 기억합니까? 어느 것이 무엇입니까? 😄
따라서 UnmarshalJSON 메서드에서 관리하겠습니다.
또한 여기에도 존재하는 이전 예제의 문제 4를 해결할 것입니다.
type BidAsk struct {
// Tags are not needed.
Price float64 `json:"price"` // Bid or Ask price
Amount float64 `json:"amount"` // Bid or Ask amount
}
func (t *BidAsk) UnmarshalJSON(data []byte) error {
// Ignore null, like in the main JSON package.
if string(data) == "null" || string(data) == `""` {
return nil
}
// Unmarshal to real type.
var bisask []string
if err := json.Unmarshal(data, &bisask); err != nil {
return err
}
// Change value type from string to float64.
price, err := strconv.ParseFloat(bisask[0], 64)
if err != nil {
return err
}
amount, err := strconv.ParseFloat(bisask[1], 64)
if err != nil {
return err
}
// Map old structure to new structure.
*t = BidAsk{
Price: price,
Amount: amount,
}
return err
}
type MarketDepth struct {
Asks []BidAsk `json:"asks"` // Ask depth
Bids []BidAsk `json:"bids"` // Bid depth
}
다시, 우리는 단순히 다음을 사용합니다.
var marketDepth MarketDepth
json.NewDecoder(raw_response.Body).Decode(&marketDepth)
결과의 아름다움을 즐기십시오.
for i, ask := range data.Data.Asks {
fmt.Printf("Ask %v\n", i)
fmt.Printf(" Price: %v\n", ask.Price) // here is the beauty
fmt.Printf(" Amount: %v\n", ask.Amount) // here is the beauty
fmt.Println()
}
for i, bid := range data.Data.Bids {
fmt.Printf("Bid %v\n", i)
fmt.Printf(" Price: %v\n", bid.Price) // here is the beauty
fmt.Printf(" Amount: %v\n", bid.Amount) // here is the beauty
fmt.Println()
}
Reference
이 문제에 관하여(Golang에서 커스텀 방식으로 JSON을 언마샬링하는 방법), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/arshamalh/how-to-unmarshal-json-in-a-custom-way-in-golang-42m5텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)