jsoniter

package module
v0.0.0-...-42a4f2c Latest Latest
Warning

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

Go to latest
Published: Dec 6, 2016 License: MIT Imports: 9 Imported by: 0

README

json iterator (jsoniter)

faster than DOM, more usable than SAX/StAX

Why json iterator?

1. It is faster

jsoniter can work as drop in replacement for json.Unmarshal, reflection-api is not only supported, but recommended.

for performance numbers, see https://github.com/json-iterator/go-benchmark

The reflection-api is very fast, on the same scale of hand written ones.

2. io.Reader as input

jsoniter does not read the whole json into memory, it parse the document in a streaming way. There are too many json parser only take []byte as input, this one does not require so.

3. Pull style api

jsoniter can be used like drop-in replacement of json.Unmarshal, for example

type StructOfTag struct {
    field1 string `json:"field-1"`
    field2 string `json:"-"`
    field3 int `json:",string"`
}

struct_ := StructOfTag{}
jsoniter.Unmarshal(`{"field-1": "hello", "field2": "", "field3": "100"}`, &struct_)

But it allows you to go down one level lower, to control the parsing process using pull style api (like StAX, if you know what I mean). Here is just a demo of what you can do

iter := jsoniter.ParseString(`[1,2,3]`)
for iter.ReadArray() {
  iter.ReadUint64()
}

4. Customization

Of course, you can use the low level pull api to do anything you like. But most of the time, reflection based api is fast enough. How to control the parsing process when we are using the reflection api? json.Unmarshaller is not flexible enough. Jsoniter provides much better customizability.

func Test_customize_type_decoder(t *testing.T) {
	RegisterTypeDecoder("time.Time", func(ptr unsafe.Pointer, iter *Iterator) {
		t, err := time.ParseInLocation("2006-01-02 15:04:05", iter.ReadString(), time.UTC)
		if err != nil {
			iter.Error = err
			return
		}
		*((*time.Time)(ptr)) = t
	})
	defer ClearDecoders()
	val := time.Time{}
	err := Unmarshal([]byte(`"2016-12-05 08:43:28"`), &val)
	if err != nil {
		t.Fatal(err)
	}
	year, month, day := val.Date()
	if year != 2016 || month != 12 || day != 5 {
		t.Fatal(val)
	}
}

there is no way to add json.Unmarshaller to time.Time as the type is not defined by you (type alias time.Time is not fun to use). Using jsoniter, we can.

type Tom struct {
	field1 string
}

func Test_customize_field_decoder(t *testing.T) {
	RegisterFieldDecoder("jsoniter.Tom", "field1", func(ptr unsafe.Pointer, iter *Iterator) {
		*((*string)(ptr)) = strconv.Itoa(iter.ReadInt())
	})
	defer ClearDecoders()
	tom := Tom{}
	err := Unmarshal([]byte(`{"field1": 100}`), &tom)
	if err != nil {
		t.Fatal(err)
	}
}

It is very common the input json has certain fields massed up. We want string, but it is int, etc. The old way is to define a struct of exact type like the json. Then we convert from one struct to a new struct. It is just too much work. Using jsoniter you can tweak the field conversion.

5. Minimum work to parse, use whatever fits the job

I invented this wheel because I find it is tedious to parse json which does not match the object model you want to use. Parse to map[string]interface{} is not only ugly but also slow. Parse to struct is not flexible enough to fix some field type mismatch or structure mismatch.

If use low level tokenizer/lexer to work at the token level, it is too much work, not to mention there is very few parser out there allow you to work on this level.

jsoniter pull-api is designed to be easy to use, so that you can map your data structure directly to parsing code. It is still tedious I am not going to lie to you, but easier than pure tokenizer. The real power is, you can mix the pull-api with reflection-api. For example:

\\ given [1, {"a": "b"}]
iter.ReadArray()
iter.ReadInt()
iter.ReadArray()
iter.Read(&struct_) // reflection-api

Also by using type or field callback, we can switch from reflection-api back to pull-api. The seamless mix of both styles enabled a unique new way to parse our data.

My advice is always use the reflection-api first. Unless you find pull-api can do a better job in certain area.

Why not json iterator?

jsoniter does not plan to support map[string]interface{}, period.

Documentation

Index

Constants

View Source
const (
	MaxRune   = '\U0010FFFF' // Maximum valid Unicode code point.
	RuneError = '\uFFFD'     // the "error" Rune or "Unicode replacement character"
)

Variables

View Source
var DECODERS unsafe.Pointer

Functions

func ClearDecoders

func ClearDecoders()

func RegisterFieldDecoder

func RegisterFieldDecoder(type_ string, field string, func_ DecoderFunc)

func RegisterTypeDecoder

func RegisterTypeDecoder(type_ string, func_ DecoderFunc)

func Unmarshal

func Unmarshal(data []byte, v interface{}) error

Types

type Decoder

type Decoder interface {
	// contains filtered or unexported methods
}

type DecoderFunc

type DecoderFunc func(ptr unsafe.Pointer, iter *Iterator)

type Iterator

type Iterator struct {
	Error error
	// contains filtered or unexported fields
}

func Parse

func Parse(reader io.Reader, bufSize int) *Iterator

func ParseBytes

func ParseBytes(input []byte) *Iterator

func ParseString

func ParseString(input string) *Iterator

func (*Iterator) CurrentBuffer

func (iter *Iterator) CurrentBuffer() string

func (*Iterator) Read

func (iter *Iterator) Read(obj interface{})

func (*Iterator) ReadArray

func (iter *Iterator) ReadArray() (ret bool)

func (*Iterator) ReadArrayCB

func (iter *Iterator) ReadArrayCB(cb func())

func (*Iterator) ReadBool

func (iter *Iterator) ReadBool() (ret bool)

func (*Iterator) ReadFloat32

func (iter *Iterator) ReadFloat32() (ret float32)

func (*Iterator) ReadFloat64

func (iter *Iterator) ReadFloat64() (ret float64)

func (*Iterator) ReadInt

func (iter *Iterator) ReadInt() (ret int)

func (*Iterator) ReadInt16

func (iter *Iterator) ReadInt16() (ret int16)

func (*Iterator) ReadInt32

func (iter *Iterator) ReadInt32() (ret int32)

func (*Iterator) ReadInt64

func (iter *Iterator) ReadInt64() (ret int64)

func (*Iterator) ReadInt8

func (iter *Iterator) ReadInt8() (ret int8)

func (*Iterator) ReadNull

func (iter *Iterator) ReadNull() (ret bool)

func (*Iterator) ReadObject

func (iter *Iterator) ReadObject() (ret string)

func (*Iterator) ReadObjectCB

func (iter *Iterator) ReadObjectCB(cb func(string))

func (*Iterator) ReadString

func (iter *Iterator) ReadString() (ret string)

func (*Iterator) ReadStringAsBytes

func (iter *Iterator) ReadStringAsBytes() (ret []byte)

func (*Iterator) ReadUint

func (iter *Iterator) ReadUint() (ret uint)

func (*Iterator) ReadUint16

func (iter *Iterator) ReadUint16() (ret uint16)

func (*Iterator) ReadUint32

func (iter *Iterator) ReadUint32() (ret uint32)

func (*Iterator) ReadUint64

func (iter *Iterator) ReadUint64() (ret uint64)

func (*Iterator) ReadUint8

func (iter *Iterator) ReadUint8() (ret uint8)

func (*Iterator) ReportError

func (iter *Iterator) ReportError(operation string, msg string)

func (*Iterator) Reuse

func (iter *Iterator) Reuse(input []byte) *Iterator

func (*Iterator) Skip

func (iter *Iterator) Skip()

Jump to

Keyboard shortcuts

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