У мене виникають проблеми з синтаксичним аналізом файлу JSON з API на Go, це JSON, який я хочу проаналізувати:
{"method":"stats.provider.ex",
"result":{
"addr":"17a212wdrvEXWuipCV5gcfxdALfMdhMoqh",
"current":[{
"algo":3, // algorithm number (3 = X11)
"name":"X11", // algorithm name
"suffix":"MH", // speed suffix (kH, MH, GH, TH,...)
"profitability":"0.00045845", // current profitability in BTC/suffix/Day
"data":[{ // speed object can contain following fields:
// a (accepted), rt (rejected target), rs (rejected stale),
// rd (rejected duplicate) and ro (rejected other)
// if fields are not present, speed is 0
"a":"23.09", // accepted speed (in MH/s for X11)
"rs":"0.54", // rejected speed - stale
},
"0.0001234" // balance (unpaid)
]},
... // other algorithms here
],
"past":[{
"algo":3,
"data":[
[4863234, // timestamp; multiply with 300 to get UNIX timestamp
{"a":"28.6"}, // speed object
"0" // balance (unpaid)
],[4863235,{"a":"27.4"},"0.00000345"],
... // next entries with inc. timestamps
]},
... // other algorithms here
],
"payments":[{
"amount":"0.00431400",
"fee":"0.00023000",
"TXID":"txidhere",
"time":1453538732, // UNIX timestamp
"type":0 // payment type (0 for standard NiceHash payment)
},
... // other payments here
]
}
}
Ви можете знайти більше інформації про API за цим посиланням: https://www.nicehash.com/doc-api
Проблема, з якою я стикаюся, полягає в атрибуті data:
"data":[{ // speed object can contain following fields:
// a (accepted), rt (rejected target), rs (rejected stale),
// rd (rejected duplicate) and ro (rejected other)
// if fields are not present, speed is 0
"a":"23.09", // accepted speed (in MH/s for X11)
"rs":"0.54", // rejected speed - stale
},
"0.0001234" // balance (unpaid)
]},
Через баланс (неоплачений) рядок, оскільки він не має імені, я не знаю, як зробити структуру в go.
Відповіді:
3 для відповіді № 1Здається, що цей об'єкт "даних" може бути описаний наступними типами структур (припускаючи, що його форма не відрізняється від ваших прикладів):
type Data struct {
Timestamp *int64
Speed *Speed
Balance *float64
}
type Speed struct {
Accepted *float64 `json:"a,string,omitempty"`
RejectedTarget *float64 `json:"rt,string,omitempty"`
RejectedStale *float64 `json:"rs,string,omitempty"`
RejectedDuplicate *float64 `json:"rd,string,omitempty"`
RejectedOther *float64 `json:"ro,string,omitempty"`
}
Структура "Швидкість" має теги JSON, оскільки цей об'єкт добре підходить для стандартного JSON un / marshaler.
Структура "Дані", однак, повинна реалізовувати спеціальний json.UnmarshalJSON
так що він може обробляти непарний вибір масиву JSON з різними типами для серіалізації своїх полів. Зверніть увагу, що мій зразок реалізації нижче використовує json.RawMessage
тип дещо спростити речі, дозволивши JSONunmarshaler, щоб забезпечити правильний синтаксис масиву JSON і зберегти байти кожного елемента окремо, щоб ми могли демаршалювати їх відповідно до їх відповідних типів і форм:
// Parse valid JSON arrays as "Data" by assuming one of the following shapes:
// 1: [int64, Speed, string(float64)]
// 2: [Speed, string(float64)]
func (d *Data) UnmarshalJSON(bs []byte) error {
// Ensure that the bytes contains a valid JSON array.
msgs := []json.RawMessage{}
err := json.Unmarshal(bs, &msgs)
if err != nil {
return err
}
// Parse the initial message as "Timestamp" int64, if necessary.
idx := 0
if len(msgs) == 3 {
ts, err := strconv.ParseInt(string(msgs[idx]), 10, 64)
if err != nil {
return err
}
d.Timestamp = &ts
idx++
}
// Parse the mandatory "Speed" struct per usual.
d.Speed = &Speed{}
err = json.Unmarshal(msgs[idx], &d.Speed)
idx++
if err != nil {
return err
}
// Parse the mandatory "Balance" item after trimming quotes.
balance, err := strconv.ParseFloat(string(msgs[idx][1:len(msgs[idx])-1]), 64)
if err != nil {
return err
}
d.Balance = &balance
return nil
}
Таким чином, ви можете аналізувати дійсні масиви JSON належної форми як об'єкти "Дані" приблизно так:
jsonstr := `[
[4863234, {"a":"28.6"}, "0" ],
[{"a":"23.09","rs":"0.54"},"0.0001234"]
]`
datas := []Data{}
err := json.Unmarshal([]byte(jsonstr), &datas)
if err != nil {
panic(err)
}
// datas[0] = Data{Timestamp:4863234,Speed{Accepted:28.6},Balance:0}
// datas[1] = Data{Speed{Accepted:23.09,RejectedStale:0.54},Balance:0.0001234}
Звичайно, вам також потрібно буде реалізувати json.MarshalJSON
якщо ви хочете серіалізувати об'єкти "Дані" в JSON.
1 для відповіді № 2
The data
поле у вашому об'єкті JSON має масив […]
як його значення, і у вашому прикладі цей масив має два елементи: об'єкт і рядок, очевидно містять число з плаваючою комою.
Як бачите, це масив гетерогенних типів, отже, у Go є два варіанти:
Створіть власний тип для елементів цього масиву та отримайте цей тип реалізує
encoding/json.Unmarshaler
інтерфейсПотім, використовуючи цей метод, ви можете займатися творчою інтерпретацією того, що тип даних, який ви збираєтеся вимкнути, і діяти відповідно до цього. В основному, ви "зазирнете у вхідні дані за допомогою
Decoder.Token
і потім демаршалізує весь зріз вхідного байта у значення відповідного типуМайте значення для цього
data
поле, яке буде демаршаліровано в фрагмент типу[]interface{}
а потім огляньте окремі елементи a тип вимикача або серія тверджень типу "кома".У цьому випадку об’єкт буде демаршалізований на карті типу
map[string]interface{}
, і цей рядок буде немаршальований до значення типуstring
.
В основному ці два підходи можна класифікувати як "виявляти тип під час руху" проти "демаршувати все в структури даних найбільш загальних типів і займатися справжнім набором тексту ".
Тут також є третій підхід.
По-перше, цілком може виявитися, що типи об'єктів у масиві яка цінність цього data
поля неявні з їхніх позицій у масиві. Ви можете діяти відповідно, демаршуючи вартість data
в об'єкт вашого користувацького типу, що реалізується json.Unmarshaler
, котрий
знає що є реальним типом кожного елемента даних, який він обробляє.
По-друге, з цього
{
// speed object can contain following fields:
// a (accepted), rt (rejected target), rs (rejected stale),
// rd (rejected duplicate) and ro (rejected other)
// if fields are not present, speed is 0
"a":"23.09", // accepted speed (in MH/s for X11)
"rs":"0.54", // rejected speed - stale
}
Я б сказав, що цей "об'єкт" дійсно може мати різні комбінації полів, так що для мене це виглядає як кандидат, з якого не хочуть брати участь в map[string]string
або map[string]float
, а не в деякі struct
-типізований об'єкт.