/typed

A wrapper around map[string]interface{} to provide some strong typing

Primary LanguageGoMIT LicenseMIT

Typed

Making map[string]interface{} slightly less painful

It isn't always desirable or even possible to decode JSON into a structure. That, unfortunately, leaves us with writing ugly code around map[string]interface{} and []interace{}.

This library hopes to make that slightly less painful.

Install

go get github.com/karlseguin/typed

Note

While the library could be used with any map[string]interface{}, it is tailored to be used with encoding/json. Specifically, it won't ever try to convert data, except for integers (which encoding/json treats as floats).

Usage:

A typed wrapper around a map[string]interface{} can be created in one of three ways:

// directly from a map[string]interace{}
typed := typed.New(a_map)

// from a json []byte
typed, err := typed.Json(data)

// from a file containing JSON
typed, err := typed.JsonFile(path)

Once we have a typed wrapper, we can use various functions to navigate the structure:

  • Bool(key string) bool

  • Int(key string) int

  • Float(key string) float64

  • String(key string) string

  • Object(key string) typed.Type

  • Interface(key string) interface{}

  • BoolOr(key string, defaultValue bool) bool

  • IntOr(key string, defaultValue int) int

  • FloatOr(key string, defaultValue float64) float64

  • StringOr(key string, defaultValue string) string

  • ObjectOr(key string, defaultValue map[string]interface{}) typed.Type

  • InterfaceOr(key string, defaultValue interface{}) interface{}

  • BoolMust(key string) bool

  • IntMust(key string) int

  • FloatMust(key string) float

  • StringMust(key string) string

  • ObjectMust(key string) typed.Type

  • `InterfaceMust(key string) interface{}``

  • BoolIf(key string) (bool, bool)

  • IntIf(key string) (int, bool)

  • FloatIf(key string) (float, bool)

  • StringIf(key string) (string, bool)

  • ObjectIf(key string) (typed.Type, bool)

  • InterfaceIf(key string) (interface{}, bool)

We can also extract arrays via:

  • Bools(key string) []bool
  • Ints(key string) []int
  • Ints64(key string) []int64
  • Floats(key string) []float64
  • Strings(key string) []string
  • StringsOr(key string, defaultValue []string) []string
  • BoolsOr(key string, defaultValue []bool) []bool
  • IntsOr(key string, defaultValue []int) []int
  • Ints64Or(key string, defaultValue []int64) []int64
  • FloatsOr(key string, defaultValue []float64) []float64

We can extract nested objects, other as another typed wrapper, or as a raw map[string]interface{}:

  • Object(key string) Typed

  • ObjectOr(key string, map[string]interface) Typed

  • ObjectIf(key string) (Typed, bool)

  • Objects(key string) []Typed

  • Map(key string) map[string]interface{}

  • MaoOr(key string, map[string]interface) map[string]interface{}

  • MapIf(key string) (map[string]interface{}, bool)

  • Maps(key string) []map[string]interface{}

We can extract key value pairs:

  • StringBool(key string) map[string]bool
  • StringInt(key string) map[string]int
  • StringFloat(key string) map[string]float64
  • StringString(key string) map[string]string
  • StringObject(key string) map[string]Typed

Example

package main

import (
  "fmt"
  "typed"
)

func main() {
  json := `
{
  "log": true,
  "name": "leto",
  "percentiles": [75, 85, 95],
  "server": {
    "port": 9001,
    "host": "localhost"
  },
  "cache": {
    "users": {"ttl": 5}
  },
  "blocked": {
    "10.10.1.1": true
  }
}`
  typed, err := typed.JsonString(json)
  if err != nil {
    fmt.Println(err)
  }

  fmt.Println(typed.Bool("log"))
  fmt.Println(typed.String("name"))
  fmt.Println(typed.Ints("percentiles"))
  fmt.Println(typed.FloatOr("load", 0.5))

  server := typed.Object("server")
  fmt.Println(server.Int("port"))
  fmt.Println(server.String("host"))

  fmt.Println(typed.Map("server"))

  fmt.Println(typed.StringObject("cache")["users"].Int("ttl"))
  fmt.Println(typed.StringBool("blocked")["10.10.1.1"])
}

Misc

To Bytes

ToBytes(key string) ([]byte, error) can be used to get the JSON data, as a []byte, from the Type. KeyNotFound will be returned if the key isn't valid.

Alternatively, MustBytes(key string) []byte can be used. It will panic on error.

Root Array

Support for JSON document with arrays at their root is supported. Use the JsonArray(data []byte), JsonStringArray(data string) and JsonFileArray(path string) to receive an []Typed

If the array has primitive values, the syntax is a little awkward. The key for the field is "0":

json := `[1, {"name": "leto"}, "spice"]`

typed, err := typed, err := typed.JsonStringArray(json)
if err != nil {
  panic(err)
}
println(typed[0].Int("0"))
println(typed[1].String("name"))
println(typed[2].String("0"))

JsonWriter

The JsonWriter library provides the opposite functionality: a lightweight approaching to writing JSON data.