Skip to content
Permalink
Browse files

Episode 19 - Parsing Arbitrary JSON (#145)

* Episode 19 - consuming unknown JSON input

* dropping use of go-simplejson

* updating docs to reflect not using go-simplejson

* bare link to simplejson

* Adding web page for parsing arbitrary JSON
  • Loading branch information...
arschles committed Feb 20, 2018
1 parent 2c9df78 commit a1709c37240d25c3d6bdb99b5bca56d2b028042f
@@ -28,4 +28,6 @@ glide-v0.12.0-linux-amd64.tar.gz
*.prof
episode*/vendor
episode*/episode*
episode*/gifm
www/public
episode19/vendor
@@ -0,0 +1,4 @@
build:
go build -o gifm
run:
./gifm
@@ -0,0 +1,18 @@
# Consuming Unknown JSON Input

Go in 5 Minutes, episode 19, as requested by [@tmcarr](https://github.com/tmcarr)!

This screencast teaches how to consume some unknown JSON. We don't know what it looks like
ahead of time, and we have to handle anything that comes our way.

We'll be using only the standard library for this, although we could also use some wonderful
packages out there to help. My favorite so far is Bitly's
[go-simplejson](https://godoc.org/github.com/bitly/go-simplejson).
I've always done this type of parsing myself, but I've started using `go-simplejson` and really
like it. It's well designed and very easy to use!

# Outline

1. Basics of JSON Parsing in Go
2. Handling different types with recursion :)
3. Let's see an example!
@@ -0,0 +1,8 @@
package main

import (
"log"
"os"
)

var logger = log.New(os.Stdout, "", log.Lshortfile)
@@ -0,0 +1,95 @@
package main

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

func main() {
if js, err := unmarshal(aDict); err != nil {
logger.Fatalf("error decoding (%s)", err)
} else {
if str, err := ifaceToString(js); err != nil {
logger.Fatalf("error converting js (%s)", err)
} else {
logger.Println(str)
}
}

if js, err := unmarshal(aList); err != nil {
logger.Fatalf("error decoding (%s)", err)
} else {
if str, err := ifaceToString(js); err != nil {
logger.Fatalf("error converting js (%s)", err)
} else {
logger.Println(str)
}
}

if js, err := unmarshal(anInt); err != nil {
logger.Fatalf("error decoding (%s)", err)
} else {
if str, err := ifaceToString(js); err != nil {
logger.Fatalf("error converting js (%s)", err)
} else {
logger.Println(str)
}
}

if js, err := unmarshal(aString); err != nil {
logger.Fatalf("error decoding (%s)", err)
} else {
if str, err := ifaceToString(js); err != nil {
logger.Fatalf("error converting js (%s)", err)
} else {
logger.Println(str)
}
}
}

func unmarshal(str string) (interface{}, error) {
var iface interface{}
decoder := json.NewDecoder(strings.NewReader(str))
decoder.UseNumber()
if err := decoder.Decode(&iface); err != nil {
return nil, err
}
return iface, nil
}

func ifaceToString(iface interface{}) (string, error) {
switch t := iface.(type) {
case map[string]interface{}:
strs := make([]string, len(t))
i := 0
for key, val := range t {
str, err := ifaceToString(val)
if err != nil {
return "", err
}
strs[i] = fmt.Sprintf("%s: %s", key, str)
i++
}
return "{" + strings.Join(strs, ", ") + "}", nil
case []interface{}:
strs := make([]string, len(t))
i := 0
for _, val := range t {
str, err := ifaceToString(val)
if err != nil {
return "", err
}
strs[i] = str
i++
}
return "[" + strings.Join(strs, ", ") + "]", nil
case int:
return fmt.Sprintf("%d", t), nil
case json.Number:
return fmt.Sprintf("%s", t), nil
case string:
return fmt.Sprintf(`"%s"`, t), nil
}
return "", fmt.Errorf("unsupported value %#v (%T)", iface, iface)
}
@@ -0,0 +1,13 @@
package main

const (
aDict = `{
"an_int": 1,
"a_string": "abc",
"an_array": [1, 2, "3"],
"a_dict": {"a": 1, "b": 2}
}`
aList = `[1, "2", 3, {"four": 4}]`
anInt = `1`
aString = `"1"`
)
@@ -0,0 +1,29 @@
+++
date = "2018-02-19T11:44:01-08:00"
title = "Parsing Arbitrary JSON"
type = "screencast"

+++

_Episode 19_

[@tmcarr](https://github.com/tmcarr) requested in
[#125](https://github.com/arschles/go-in-5-minutes/issues/125) that I do a screencast
on how to consume json with unknown fields.

This screencast shows how to exactly that! I'll show how to parse JSON using just the standard
library, and also point folks to a package that will help if you don't want to steal my code
(which is totally fine by the way!)

Here ya go [@tmcarr](https://github.com/tmcarr)!

<!--more-->

If you like what you see here, consider
[subscribing to the GIFM newsletter](https://www.goin5minutes.com/subscribe/).

Keep on rockin', Gophers!

<iframe width="560" height="315" src="https://www.youtube.com/embed/52yMK6p_cAg" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>

Check out the example code [on Github](https://github.com/arschles/go-in-5-minutes/tree/master/episode19).

0 comments on commit a1709c3

Please sign in to comment.
You can’t perform that action at this time.