-
Notifications
You must be signed in to change notification settings - Fork 165
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
Showing
7 changed files
with
169 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
build: | ||
go build -o gifm | ||
run: | ||
./gifm |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package main | ||
|
||
import ( | ||
"log" | ||
"os" | ||
) | ||
|
||
var logger = log.New(os.Stdout, "", log.Lshortfile) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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"` | ||
) |
29 changes: 29 additions & 0 deletions
29
www/content/screencast/episode_19_parsing_arbitrary_json.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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). |