Unmarshaling a json http response - json

I have very recently started playing with GO and I am trying to unmarshal a JSON response from http://www.oref.org.il/WarningMessages/alerts.json.
For some reason that I can't understand the unmarshaling failed, the unmarshalled struct is empty (my guess is that it is somehow related to encoding).
Below is the code, any help is appreciated.
Thanks,
Itay
package main
import (
"fmt"
"io/ioutil"
"net/http"
"encoding/json"
)
const alertsUrl = "http://www.oref.org.il/WarningMessages/alerts.json"
type Record struct {
Id string `json:id`
Title string `json:title`
Data []string `json:data`
}
func main() {
client := &http.Client{}
req, err := http.NewRequest("GET", alertsUrl, nil)
perror(err)
req.Header.Add("Content-Type", "application/json; charset=utf-8")
res, err := client.Do(req)
perror(err)
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
perror(err)
var record Record
json.Unmarshal(body, &record)
fmt.Println(record)
}
func perror(err error) {
if err != nil {
panic(err)
}
}

You're ignoring the error on JSON Unmarshal:
func Unmarshal(data []byte, v interface{}) error
See that it returns an error. Adding that in,
err = json.Unmarshal(body, &record)
perror(err)
It looks like this is a Unicode error — you need to decode the UTF-16 data.
How should you do this? Take a look at this answer. Basically once you read the body like body, err := ioutil.ReadAll(res.Body), you want to decode the UTF-16 bytes to a string. There is a lot going on there, but we can take some liberties: for instance, pulling up the URL in Chrome, the browser tells us that it is UTF-16 LE. So we can skip the ByteOrder detection. So the key here is this function:
func UTF16BytesToString(b []byte, o binary.ByteOrder) string {
utf := make([]uint16, (len(b)+(2-1))/2)
for i := 0; i+(2-1) < len(b); i += 2 {
utf[i/2] = o.Uint16(b[i:])
}
if len(b)/2 < len(utf) {
utf[len(utf)-1] = utf8.RuneError
}
return string(utf16.Decode(utf))
}
Knowing our byte order and passing it in, this will convert the naïve byte array to a string of UTF-16 characters. Thanks to user OneOfOne's comment, we can also detect the BOM easily.
The result:
package main
import (
"encoding/binary"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"unicode/utf16"
"unicode/utf8"
)
const alertsUrl = "http://www.oref.org.il/WarningMessages/alerts.json"
type Record struct {
Id string `json:id`
Title string `json:title`
Data []string `json:data`
}
// LazyUTF16BytesToString converts UTF-16 encoded bytes, in big or little endian byte order,
// to a UTF-8 encoded string.
func LazyUTF16BytesToString(b []byte) string {
if len(b)%2 != 0 {
panic("len(b) % 2 != 0")
}
var codec binary.ByteOrder = binary.LittleEndian
if b[0] == 0xFE && b[1] == 0xFF { //check and strip the BOM
b = b[2:]
codec = binary.BigEndian
} else if b[0] == 0xFF && b[1] == 0xFE {
b = b[2:]
}
utf := make([]uint16, (len(b)+(2-1))/2)
for i := 0; i+(2-1) < len(b); i += 2 {
utf[i/2] = codec.Uint16(b[i:])
}
if len(b)/2 < len(utf) {
utf[len(utf)-1] = utf8.RuneError
}
return string(utf16.Decode(utf))
}
func main() {
client := &http.Client{}
req, err := http.NewRequest("GET", alertsUrl, nil)
perror(err)
req.Header.Add("Content-Type", "application/json; charset=utf-8")
res, err := client.Do(req)
perror(err)
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
perror(err)
bodyString := LazyUTF16BytesToString(body)
var record Record
err = json.Unmarshal([]byte(bodyString), &record)
perror(err)
fmt.Println(record)
}
func perror(err error) {
if err != nil {
panic(err)
}
}

Related

Chi empty http.Request.Body in render.Bind

I am using github.com/pressly/chi to build this simple program where I try to decode some JSON from the http.Request.Body:
package main
import (
"encoding/json"
"fmt"
"net/http"
"github.com/pressly/chi"
"github.com/pressly/chi/render"
)
type Test struct {
Name string `json:"name"`
}
func (p *Test) Bind(r *http.Request) error {
err := json.NewDecoder(r.Body).Decode(p)
if err != nil {
return err
}
return nil
}
func main() {
r := chi.NewRouter()
r.Post("/products", func(w http.ResponseWriter, r *http.Request) {
var p Test
// err := render.Bind(r, &p)
err := json.NewDecoder(r.Body).Decode(&p)
if err != nil {
panic(err)
}
fmt.Println(p)
})
http.ListenAndServe(":8080", r)
}
When I don't use render.Bind() (from "github.com/pressly/chi/render"), it works as expected.
However, when I uncomment the line err := render.Bind(r, &p) and I comment the line err := json.NewDecoder(r.Body).Decode(&p), it panics with EOF :
2017/06/20 22:26:39 http: panic serving 127.0.0.1:39696: EOF
and thus the json.Decode() fails.
Am I doing something wrong or is the http.Request.Body is already read somewhere else before render.Bind() is called?
render.Bind's purpose is to perform decode and execute Bind(r) to do post decode operations.
For eg.:
type Test struct {
Name string `json:"name"`
}
func (p *Test) Bind(r *http.Request) error {
// At this point, Decode is already done by `chi`
p.Name = p.Name + " after decode"
return nil
}
If you have to do only JSON decode no other actions needs to be done after decode with respect to decoded values. Just use:
// Use Directly JSON decoder of std pkg
err := json.NewDecoder(r.Body).Decode(&p)
OR
// Use wrapper method from chi DecodeJSON
err := render.DecodeJSON(r.Body, &p)

GoLang Json FXCM

Why is this not dumping out the string? Anyone have any ideas how to get this code working?
package main
import (
"bytes"
"encoding/json"
"fmt"
)
type Tick struct {
Query string `json:"query"`
}
func main() {
data := &Tick{Query: "https://ratesjson.fxcm.com/DataDisplayer?&callback=Tick"}
buf := new(bytes.Buffer)
enc := json.NewEncoder(buf)
enc.SetEscapeHTML(true)
_ = enc.Encode(data)
fmt.Println(string(buf.Tick()))
fmt.Println("Done")
}
Anyone know why this is invalid json or why this can not be parsed? Or point out the fix how to make this work?
package main
import (
"log"
"fmt"
"net/http"
"bytes"
"io/ioutil"
"github.com/pquerna/ffjson/ffjson"
)
type MsgRatesArray struct {
RateQuote []MsgRateQuoteJson `json:"Rates"`
}
type MsgRateQuoteJson struct {
SymbolName string `json:"Symbol"`
SymbolBid int64 `json:"Bid"`
SymbolAsk int64 `json:"Ask"`
SymbolSpread int64 `json:"Spread"`
SymbolPT string `json:"ProductType"`
}
var respBytes []byte
func main() {
var msg MsgRatesArray
response,err := http.Get("https://ratesjson.fxcm.com/DataDisplayer?&callback=Tick")
if err != nil {
log.Fatal(err)
}
defer response.Body.Close()
respBytes, err := ioutil.ReadAll(response.Body)
jsonBytes := respBytes[bytes.Index(respBytes, []byte("{")):bytes.LastIndex(respBytes, []byte("}"))+1]
jsonString := string(jsonBytes)
fmt.Println(jsonString)
err = ffjson.Unmarshal(jsonBytes, &msg)
if err != nil {
panic(err)
}
}
Do your own http request to get the json, then strip out the non json stuff (everything before the first { and after the last }:
response,err := http.Get("https://ratesjson.fxcm.com/DataDisplayer?&callback=Tick")
if err != nil {
log.Fatal(err)
}
defer response.Body.Close()
respBytes, err := ioutil.ReadAll(response.Body)
jsonBytes := respBytes[bytes.Index(respBytes, []byte("{")):bytes.LastIndex(respBytes, []byte("}"))+1]
jsonString := string(jsonBytes)
fmt.Println(jsonString)
https://play.golang.org/p/JyibZ3g6UA

Golang - Cannot access map in []interface{}

I have used json.Unmarshal and extracted json content. I then managed to get one layer deeper into the []interface{} by using the following code:
response, err := http.Get("http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=2B2A0C37AC20B5DC2234E579A2ABB11C&steamids=76561198132612090")
content, err := ioutil.ReadAll(response.Body)
defer response.Body.Close()
if err != nil {
panic(0)
}
var decoded map[string]interface{}
if err := json.Unmarshal(content, &decoded); err != nil {
panic(0)
}
players := decoded["response"].(map[string]interface{})["players"]
if err != nil {
panic(0)
}
Variable players' type is []interface {} and content is [map[personaname:Acidic]].
How do I access this map? I've tried players["personaname"] but that doesn't seem to work. Any ideas?
Defining a struct type with the expected schema will make your life easier when you want to get the data from it:
package main
import "fmt"
//import "net/http"
//import "io/ioutil"
import "encoding/json"
// you don't need to define everything, only what you need
type Player struct {
Steamid string
Communityvisibilitystate int
Personaname string
Lastlogoff int64 // time.Unix(Lastlogoff, 0)
Profileurl string
Avatar string
Avatarmedium string
Avatarfull string
Personastate int
Realname string
Primaryclanid string
Timecreated int64 // time.Unix(Timecreated, 0)
Personastateflags int
//Loccountrycode string // e.g. if you don't need this
}
func main() {
/*response, err := http.Get("http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=2B2A0C37AC20B5DC2234E579A2ABB11C&steamids=76561198132612090")
if err != nil {
panic(err)
}
content, err := ioutil.ReadAll(response.Body)
defer response.Body.Close()
if err != nil {
panic(0)
}*/
content := []byte(`{
"response": {
"players": [
{
"steamid": "76561198132612090",
"communityvisibilitystate": 3,
"profilestate": 1,
"personaname": "Acidic",
"lastlogoff": 1459489924,
"profileurl": "http://steamcommunity.com/id/ari9/",
"avatar": "https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/bc/bc50a4065c31c606e51dfad329341b2d1f1ac4d3.jpg",
"avatarmedium": "https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/bc/bc50a4065c31c606e51dfad329341b2d1f1ac4d3_medium.jpg",
"avatarfull": "https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/bc/bc50a4065c31c606e51dfad329341b2d1f1ac4d3_full.jpg",
"personastate": 3,
"realname": "Ari Seyhun",
"primaryclanid": "103582791440552060",
"timecreated": 1397199406,
"personastateflags": 0,
"loccountrycode": "TR"
}
]
}
}`)
var decoded struct {
Response struct {
Players []Player
}
}
if err := json.Unmarshal(content, &decoded); err != nil {
panic(err)
}
fmt.Printf("%#v\n", decoded.Response.Players)
}
http://play.golang.org/p/gVPRwLFunF
You can also create a new named type from time.Time for Timecreated and Lastlogoff with its own UnmarshalJSON function, and immediately convert it to time.Time using time.Unix()
Players is a JSON array. Thus you have to convert it to a slice of interface.
Then you can access any element of the slice and casting it to a map[string]interface{} type.
Here's the working example
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)
func main() {
response, err := http.Get("http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=2B2A0C37AC20B5DC2234E579A2ABB11C&steamids=76561198132612090")
content, err := ioutil.ReadAll(response.Body)
defer response.Body.Close()
if err != nil {
panic(0)
}
var decoded map[string]interface{}
if err := json.Unmarshal(content, &decoded); err != nil {
panic(0)
}
players := decoded["response"].(map[string]interface{})["players"]
if err != nil {
panic(0)
}
sliceOfPlayers := players.([]interface{})
fmt.Println((sliceOfPlayers[0].(map[string]interface{}))["personaname"])
}

Golang json query from bitcoin api returns invalid character

Something tells me I'm not understanding json correctly. I'm trying to grab some data off http://api.bitcoincharts.com/v1/trades.csv?symbol=rockUSD, but my Unmarshal seems to not be able to read the json data. I'm a fresh beginner to golang (and json as well), and I'm wondering how I am able to skip that wrong character error I'm making.
My error:
invalid character ',' after top-level value
panic: invalid character ',' after top-level value
My code:
package main
import ("fmt"
"net/http"
"io/ioutil"
"encoding/json"
)
type Prices struct {
Data string
}
func main() {
url := "http://api.bitcoincharts.com/v1/trades.csv?symbol=rockUSD"
httpresp, err := http.Get(url)
if err != nil{
fmt.Println(err)
panic(err)
}
defer httpresp.Body.Close()
htmldata, err := ioutil.ReadAll(httpresp.Body)
if err != nil{
fmt.Println(err)
panic (err)
}
var jsonData []Prices
err = json.Unmarshal([]byte(htmldata), &jsonData)
if err != nil {
fmt.Println(err)
panic (err)
}
fmt.Println(jsonData)
}
That is NOT json data at all, you'd have to write a custom parser.
Example:
.........
data := readData(httpresp.Body)
........
func readData(r io.Reader) (out [][3]float64) {
br := bufio.NewScanner(r)
for br.Scan() {
parts := strings.Split(br.Text(), ",")
if len(parts) != 3 {
continue
}
var fparts [3]float64
for i, p := range parts {
// bad idea to ignore errors, but it's left as exercise for the reader.
fparts[i], _ = strconv.ParseFloat(p, 64)
}
out = append(out, fparts)
}
return
}
playground

Golang Converting JSON

map[key:2073933158088]
I need to grab the key out of this data structure as a string, but I can't seem to figure out how!
Help with this overly simple question very much appreciated.
The value above is encapsulated in the variable named data.
I have tried: data.key, data[key], data["key"], data[0] and none of these seem to be appropriate calls.
To define data I sent up a JSON packet to a queue on IronMQ. I then pulled the message from the queue and manipulated it like this:
payloadIndex := 0
for index, arg := range(os.Args) {
if arg == "-payload" {
payloadIndex = index + 1
}
}
if payloadIndex >= len(os.Args) {
panic("No payload value.")
}
payload := os.Args[payloadIndex]
var data interface{}
raw, err := ioutil.ReadFile(payload)
if err != nil {
panic(err.Error())
}
err = json.Unmarshal(raw, &data)
Design your data type to match json structure. This is how can you achieve this:
package main
import (
"fmt"
"encoding/json"
)
type Data struct {
Key string `json:"key"`
}
func main() {
data := new(Data)
text := `{ "key": "2073933158088" }`
raw := []byte(text)
err := json.Unmarshal(raw, data)
if err != nil {
panic(err.Error())
}
fmt.Println(data.Key)
}
Since the number in the json is unquoted, it's not a string, Go will panic if you try to just handle it as a string (playground: http://play.golang.org/p/i-NUwchJc1).
Here's a working alternative:
package main
import (
"fmt"
"encoding/json"
"strconv"
)
type Data struct {
Key string `json:"key"`
}
func (d *Data) UnmarshalJSON(content []byte) error {
var m map[string]interface{}
err := json.Unmarshal(content, &m)
if err != nil {
return err
}
d.Key = strconv.FormatFloat(m["key"].(float64), 'f', -1, 64)
return nil
}
func main() {
data := new(Data)
text := `{"key":2073933158088}`
raw := []byte(text)
err := json.Unmarshal(raw, data)
if err != nil {
panic(err.Error())
}
fmt.Println(data.Key)
}
You can see the result in the playground: http://play.golang.org/p/5hU3hdV3kK