lzjson

package module
v0.0.0-...-efe3c53 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 6, 2016 License: MIT Imports: 7 Imported by: 4

README

lzjson GoDoc Travis CI results AppVeyor Coverage Status

lzjson is a JSON decoding library aims to make you lazy.

Golang default JSON library requires to provide certain data structure (e.g. struct) to decode data to. It is hard to write type-inspecific code to examine JSON data structure. It is also hard to determine the abcence or prescence of data field.

This library provide flexible interface for writing generic JSON parsing code.

Key features:

  • zero knowledge parsing: can read and examine JSON structure without pre-defining the data structure before hand.

  • lazy parsing: allow you to parse only a specific node into golang data structure.

  • compatibility: totally compatible with the default json library

Example Use

Decode a JSON

Decode is straight forward with any io.Reader implementation (e.g. http.Request.Body, http.Response.Body, strings.Reader).

For example, in a http.HandlerFunc:


import (
  "net/http"

  "github.com/go-restit/lzjson"
)


func handler(w http.ResponseWriter, r *http.Request) {
  json := lzjson.Decode(r.Body)
  ...
  ...
}

Or as a client:

func main() {
  resp, _ := http.Get("http://foobarapi.com/things")
  json := lzjson.Decode(resp.Body)
  ...
  ...
}
Get a node in an object or an array

You may retrieve the JSON value of any node.

// get "foo" in the json
foo := json.Get("foo")

// get the 10th item in foo
// (like ordinary array, 0 is the first)
item10 := foo.GetN(9)
Every node knows what it is
body := strings.NewReader(`
{
  "string": "hello world",
  "number": 3.14,
  "bool": true,
  "array": [1, 2, 3, 5],
  "object": {"foo": "bar"}
}
`)
json := lzjson.Decode(body)

fmt.Printf("%s", json.Get("string").Type()) // output "TypeString"
fmt.Printf("%s", json.Get("number").Type()) // output "TypeNumber"
fmt.Printf("%s", json.Get("bool").Type())   // output "TypeBool"
fmt.Printf("%s", json.Get("array").Type())  // output "TypeArray"
fmt.Printf("%s", json.Get("object").Type()) // output "TypeObject"
Evaluating values a JSON node

For basic value types (string, int, bool), you may evaluate them directly.

code := json.Get("code").Int()
message := json.Get("message").String()
Partial Unmarsaling

You may decode only a child-node in a JSON structure.


type Item struct {
  Name   string `json:"name"`
  Weight int    `json:"weight"`
}

var item Item
item10 := foo.GetN(9)
item10.Unmarshal(&item)
log.Printf("item: name=%s, weight=%d", item.Name, item.Weight)

Chaining

You may chain Get and GetN to get somthing deep within.


helloIn10thBar := lzjson.Decode(r.Body).Get("foo").GetN(9).Get("hello")

Looping Object or Array

Looping is straight forward with Len and GetKeys.

var item Item
for i := 0; i<foo.Len(); i++ {
  foo.Get(i).Unmarshal(&item)
  log.Printf("i=%d, value=%#v", i, item)
}

for _, key := range json.GetKeys() {
  log.Printf("key=%#v, value=%#v", key, json.Get(key).String())
}
Error knows their location

With chaining, it is important where exactly did any parse error happen.


body := strings.NewReader(`
{
  "hello": [
    {
      "name": "world 1"
    },
    {
      "name": "world 2"
    },
    {
      "name": "world 3"
    },
  ],
}
`)
json := lzjson.Decode(body)

inner := json.Get("hello").GetN(2).Get("foo").Get("bar").GetN(0)
if err := inner.ParseError(); err != nil {
  fmt.Println(err.Error()) // output: "hello[2].foo: undefined"
}

Full Example

Put everything above together, we can do something like this:


package main

import (
  "log"
  "net/http"

  "github.com/go-restit/lzjson"
)

type Thing struct {
  ID        string    `json:"id"`
  Name      string    `json:"name"`
  Found     time.Time `json:"found"`
  FromEarth bool      `json:"from_earth"`
}

/**
 * assume the API endpoints returns data:
 * {
 *   "code": 200,
 *   "data": [
 *     ...
 *   ]
 * }
 *
 * or error:
 * {
 *   "code": 500,
 *   "message": "some error message"
 * }
 *
 */
func main() {
  resp, err := http.Get("http://foobarapi.com/things")
  if err != nil {
    panic(err)
  }

  // decode the json as usual, if no error
  json := lzjson.Decode(resp.Body)
  if code := json.Get("code").Int(); code != 200 {
    message := json.Get("message").String()
    log.Fatalf("error %d: ", code, message)
  }

  // get the things array
  things := json.Get("data")

  // loop through the array
  for i := 0; i<things.Len(); i++ {
    thing := things.GetN(i)
    if err := thing.ParseError(); err != nil {
      log.Fatal(err.Error())
    }

    // if the thing is not from earth, unmarshal
    // as a struct then read the details
    if !thing.Get("from_earth").Bool() {
      var theThing Thing
      thing.Unmarshal(&theThing)
      log.Printf("Alien found! %#v", theThing)
    }
  }

}

For more details, please read the documentation

Contirbuting

Your are welcome to contribute to this library.

To report bug, please use the issue tracker.

To fix an existing bug or implement a new feature, please:

  1. Check the issue tracker and pull requests for existing discussion.
  2. If not, please open a new issue for discussion.
  3. Write tests.
  4. Open a pull request referencing the issue.
  5. Have fun :-)

Licence

This software is licenced with the [MIT Licence] licence. You can obtain a copy of the licence in this repository.

Documentation

Overview

Example
package main

import (
	"fmt"
	"io"
	"strings"

	"github.com/go-restit/lzjson"
)

func dummyBody() io.Reader {
	return strings.NewReader(`{
    "hello": [
      {
        "name": "world 1",
        "size": 123
      },
      {
        "name": "world 2"
      },
      {
        "name": "world 3"
      }
    ]
  }`)
}

type Namer struct {
	Name string `json:"name"`
}

func main() {
	body := dummyBody()
	data := lzjson.Decode(body)

	// reading a certain node in the json is straight forward
	fmt.Println(data.Get("hello").GetN(1).Get("name").String())      // output "world 2"
	fmt.Printf("%#v\n", data.Get("hello").GetN(0).Get("size").Int()) // output "123"

	// you may unmarshal the selected child item without defining parent struct
	var namer Namer
	data.Get("hello").GetN(2).Unmarshal(&namer)
	fmt.Println(namer.Name) // output "world 3"

	// you may count elements in an array without defining the array type
	if err, count := data.Get("hello").ParseError(), data.Get("hello").Len(); err == nil {
		fmt.Printf("numbers of item in json.hello: %#v\n", count) // output "numbers of item in json.hello: 3"
	}

	// parse errors inherit along the path, no matter how deep you went
	if err := data.Get("foo").GetN(0).ParseError(); err != nil {
		fmt.Println(err.Error()) // output "json.foo: undefined"
	}
	if err := data.Get("hello").Get("notexists").Get("name").ParseError(); err != nil {
		fmt.Println(err.Error()) // output "json.hello: not an object"
	}
	if err := data.Get("hello").GetN(0).Get("notexists").Get("name").ParseError(); err != nil {
		fmt.Println(err.Error()) // output "json.hello[0].notexists: undefined"
	}
	if err := data.Get("hello").GetN(10).GetN(0).Get("name").ParseError(); err != nil {
		fmt.Println(err.Error()) // output "json.hello[10]: undefined"
	}

}
Output:

world 2
123
world 3
numbers of item in json.hello: 3
json.foo: undefined
json.hello: not an object
json.hello[0].notexists: undefined
json.hello[10]: undefined

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Error

type Error struct {
	Path string
	Err  error
}

Error is the generic error for parsing

func (Error) Error

func (err Error) Error() string

Error implements error type

func (Error) String

func (err Error) String() string

String implements Stringer

type Node

type Node interface {

	// Unmarshal parses the JSON node data into variable v
	Unmarshal(v interface{}) error

	// UnmarshalJSON implements json.Unmarshaler
	UnmarshalJSON(b []byte) error

	// Raw returns the raw JSON string in []byte
	Raw() []byte

	// Type returns the Type of the containing JSON value
	Type() Type

	// GetKeys gets an array object's keys,
	// or nil if not an object
	GetKeys() []string

	// Get gets object's inner value.
	// Only works with Object value type
	Get(key string) (inner Node)

	// Len gets the length of the value
	// Only works with Array and String value type
	Len() int

	// GetN gets array's inner value.
	// Only works with Array value type.
	// 0 for the first item.
	GetN(nth int) Node

	// String unmarshal the JSON into string then return
	String() (v string)

	// Number unmarshal the JSON into float64 then return
	Number() (v float64)

	// Int unmarshal the JSON into int the return
	Int() (v int)

	// Bool unmarshal the JSON into bool then return
	Bool() (v bool)

	// IsNull tells if the JSON value is null or not
	IsNull() bool

	// ParseError returns the JSON parse error, if any
	ParseError() error
}

Node is an interface for all JSON nodes

func Decode

func Decode(reader io.Reader) Node

Decode read and decodes a JSON from io.Reader then returns a Node of it

func NewNode

func NewNode() Node

NewNode returns an initialized empty Node value ready for unmarshaling

type ParseError

type ParseError int

ParseError describe error natures in parsing process

const (
	ErrorUndefined ParseError = iota
	ErrorNotObject
	ErrorNotArray
)

types of error

func (ParseError) Error

func (err ParseError) Error() string

func (ParseError) GoString

func (err ParseError) GoString() string

GoString implements fmt.GoStringer

func (ParseError) String

func (i ParseError) String() string

type Type

type Type int

Type represents the different type of JSON values (string, number, object, array, true, false, null) true and false are combined as bool for obvious reason

const (
	TypeError     Type = -1
	TypeUndefined Type = iota
	TypeString
	TypeNumber
	TypeObject
	TypeArray
	TypeBool
	TypeNull
)

These constant represents different JSON value types as specified in http://www.json.org/ with some exception: 1. true and false are combined as bool for obvious reason; and 2. TypeUnknown for empty strings

func (Type) GoString

func (t Type) GoString() string

GoString implements fmt.GoStringer

func (Type) String

func (i Type) String() string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL