/ / Розбір JSON в Go Lang: Атрибут без імені - json, go

Аналіз JSON в Go Lang: Атрибут без імені - json, go

У мене виникають проблеми з синтаксичним аналізом файлу 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-типізований об'єкт.