Help us shape the future of Go! Take the 2026 Go Developer Survey

json

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Aug 23, 2020 License: GPL-3.0 Imports: 8 Imported by: 0

README

Documentation Build Status CircleCI codecov GolangCI Go Report Card Project status

json - Fast but Flexible JSON library for golang

This is one more json decoding/encoding library. Why should you use it? Because it's

  • Fast
  • Memory efficient
  • Supports Unmarshal interface as well as raw data iterators
  • Supports decoding/encoding from/to memory buffer as well as io.Reader/Writer
  • Tested by unit tests, benchmarks and fuzzing
  • It doesn't do what you didn't ask saving time and resources
  • It can unmarshal struct and don't zero fields you didn't ask (which weren't present in data)

If you don't have go2doc than use a link https://godoc.org/github.com/nikandfor/json

Status

It's mostly done but I'm still may change something, including api. Although it tested and even fuzzed (https://github.com/dvyukov/go-fuzz) it's used only at my projects by now, so some situations that are not crashes but getting unexpected results may happen. So error reports are welcome. If you miss some feature feel free to ask for it either.

Examples

Conventional Unmarshal works as usual

err := json.Unmarshal(data, &result)

Not cleaning fields UnmarshalNoZero looks similar

err := json.UnmarshalNoZero(data, &result)

Perhaps you need only one subobject at document, easily and affectivly

err := json.Wrap(data).Get("users", 0, "profile").Unmarshal(&result)

Or you need only one field

city, err := json.Wrap(data).Get("users", 0, "profile", "location", "city").CheckString()

You may do the same for stream of objects (like application/json-seq)

r := json.NewReader(stream)
for r.Type() != json.None {
    city, err := r.Get("profile", "location", "city").CheckString()
    if err != nil {
        break
    }
    _ = city
    r.GoOut(3) // read object to the end on 3 levels back (it's the depth we've got to by Get(...))
}

Or you can inspect an Array

r.Get("profile", "languages")
for r.HasNext() {
    lang := r.NextString()
    _ = lang
}

or Object

r.Get("profile", "health")
for r.HasNext() {
    key := r.NextString()
    switch string(key) {
    case "blood_pressure":
        _ = r.NextNumber()
    case "blood_type":
        _ = r.NextString()
    default:
        r.Skip() // don't forget to read unused values
    }
}

It's a single pass algorithm, but you can say it to remember position and return

r.Lock()
val, err := r.Get("check","object","key").CheckString()
// ...
if ... {
    r.Unlock()
    return
}
r.Return()

err = r.Unmarshal(wanted_object)
// ...

Remember that all bytes that are locked are kept in memory, don't forget to unlock it when you are done

You can parse object in raw format without knowing structure (Full example at godoc under Reader.Type() method)

parse = func(r *json.Reader, d int) {
    switch tp := r.Type(); tp {
    case json.String, json.Number, json.Bool, json.Null:
        // read by one of these accordingly
        //   r.CheckString()
        //   r.Int() or r.Float64()
        //   r.Bool()
        //   r.Skip() // you can skip any value like this
        val := r.NextBytes() // reads any value including object and array as raw bytes
        fmt.Printf("%*s is %v\n", d*4, val, tp)
    case json.ArrayStart:
        for r.HasNext() {
            parse(r, d+1)
        }
    case json.ObjStart:
        for r.HasNext() {
            key := r.NextString()
            fmt.Printf("%*s ->\n", d*4, key)
            parse(r, d+1)
        }
    default:
        err := r.Err()
        fmt.Printf("%*s got type %v err %#v\n", d*4, "", tp, err)
    }
}

data := `{"first": "string", "second": 123, "third": [1.1, 3.3, 7.7], "fourth": {"again": "string", "and": {"object": "here"}}}`
r := json.Wrap(data)

parse(r)

// Output:
// first ->
// "string" is String
// second ->
//      123 is Number
// third ->
//          1.1 is Number
//          3.3 is Number
//          7.7 is Number
// fourth ->
//    again ->
//     "string" is String
//      and ->
//       object ->
//           "here" is String

As you may noticed I didn't checked for errors. That's because I can do it only once at the end. Imagine we have typo in out previous example: first quote mark of fourth key is wrong.

data := `{"first": "string", "second": 123, "third": [1.1, 3.3, 7.7], 'fourth": {"again": "string", "and": {"object": "here"}}}`
// ...
if err := r.Err(); err != nil {
    fmt.Printf("reader: %#v\n", err)
}
// Output:
// reader: parse error at pos 61: invalid character `.1, 3.3, 7.7], 'fourth": {"agai`

And we've got error message with position and context. Errors could be printed an even better.

// ...
if err := r.Err(); err != nil {
    fmt.Printf("reader: %+40v\n", err) // here we can choose extended format and context size (works for # either)
}
// Output:
// reader: parse error at pos 61: invalid character
// ...[1.1, 3.3, 7.7], 'fourth": {"again"...
// ____________________^____________________

Of course it works only with monospace fonts.

Errors Checking

To tell the truth I must say that it doesn't tell you anything if you missed , or :, or some number you skipped was like this 45.67+13e44 (it does well if you parse numbers with r.Int() or r.Float64() or so methods, or unmarshal them). All the errors that could be ignored are ignored. That's done for the sake of performance.

Performance

Performance tests are the same as jsoniter and json parser have. And they are my closest competitors.

Medium size json object decoding

// for loop with single switch and for b[i] != '"' {i++} string skipper
BenchmarkRawLoopStructMedium-8                	 1000000	      1907 ns/op	1220.62 MB/s	       0 B/op	       0 allocs/op

// Unmarshal struct
BenchmarkDecodeStdStructMedium-8              	   50000	     31848 ns/op	  73.10 MB/s	    1960 B/op	      99 allocs/op
BenchmarkDecodeJsoniterStructMedium-8         	  200000	      6446 ns/op	 361.13 MB/s	     480 B/op	      45 allocs/op
BenchmarkDecodeEasyJsonMedium-8               	  200000	      7902 ns/op	 294.58 MB/s	     160 B/op	       4 allocs/op
BenchmarkDecodeNikandjsonStructMedium-8       	  200000	      6354 ns/op	 366.35 MB/s	      80 B/op	       2 allocs/op
// Buger don't have such function

// Get single key
BenchmarkDecodeBugerGetStructMedium-8         	  500000	      3495 ns/op	 665.93 MB/s	       0 B/op	       0 allocs/op
BenchmarkDecodeNikandjsonGetStructMedium-8    	  300000	      4475 ns/op	 520.12 MB/s	       0 B/op	       0 allocs/op
// others don't support such function

Large size json object decoding

// count array elements in large json
BenchmarkBugerJsonParserLarge-8    	   50000	     34488 ns/op	 815.43 MB/s	       0 B/op	       0 allocs/op
BenchmarkJsoniterLarge-8           	   20000	     94858 ns/op	 296.47 MB/s	   12976 B/op	    1133 allocs/op
BenchmarkNikandjsonGetLarge-8      	   30000	     52134 ns/op	 539.43 MB/s	       0 B/op	       0 allocs/op

As you can see my library is not the fastest, but it is as memory efficient as possible (as Buger as) and it has all the functions in single package. Two allocs in BenchmarkDecodeNikandjsonStructMedium test is []byte to string conversion when struct fields are set (that could be avoided by unsafe hack, but in that situation it's not worth it).

There are two skipString function implementations: strict (default) that checks all strings to utf8 encoding correctness (it's still done for strings you get by NextString and CheckString methods) and fast that doesn't. It could be chosen at compilation time by setting build tag unsafestrings.

Documentation

Overview

Package json is an faster alternative to stdlib

It allows you to parse bytes buffer or io.Reader stream with no allocs and without knowing document structure and field names. You also can do the same in opposite direction: building json in a streaming fashion.

It's could be used as a replacement to "encoding/json". Or you can build complex analyzer on top of it.

It's an one time reader, so read data are dropped if buffer gets full.

It's doesn't do things you don't need. It won't parse or read anything until you ask. It doesn't decode escape sequences at strings you don't need. It copy readed string only if it contain escape sequences or if it doesn't fit info the buffer. And it's copied to reusable buffer. There are two versions on skipString functions: fast (default), that looks only for closing '"' byte, and strict, that check utf8 encoding also (you can choose between them by strict build tag). Reader.CompareKey doesn't check utf8 encoding and doesn't decode escape sequences (did you see object keys like this "\t\nkey \n"? if so, use (*Reader).NextString() instead).

You should be very attentive since some checks were sacrificed in favor of speed. So you will not be notified if for example you forgot to read some value in the object and requested the next key.

See examples for more ideas of usage

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	ErrIncompatibleTypes = errors.New("incompatible types")
	ErrEncoding          = errors.New("string encoding")
)

Functions

func Cause added in v0.2.0

func Cause(err error) error

Cause returns cause of underlying error if any

copy of github.com/pkg/errors.Cause

func Copy added in v0.2.0

func Copy(w *Writer, r *Reader)

Copy copies one next value from r into w

func UnsafeStringToBytes added in v0.2.0

func UnsafeStringToBytes(s string) []byte

Types

type Base64Reader added in v0.2.0

type Base64Reader struct {
	StringReader
	// contains filtered or unexported fields
}

Base64Reader allows to read raw bytes stream encoded in base64 encoding

func (Base64Reader) Read added in v0.2.0

func (r Base64Reader) Read(p []byte) (int, error)

Read reads data from a big string value and decodes it from base64 encoding It returns io.EOF if entire string has been read

type Base64Writer added in v0.2.0

type Base64Writer struct {
	StringWriter
	// contains filtered or unexported fields
}

Base64Writer allows to write raw bytes stream as an base64 encoded string

func (Base64Writer) Close added in v0.2.0

func (w Base64Writer) Close() error

Close must be called to finish the string

func (Base64Writer) Write added in v0.2.0

func (w Base64Writer) Write(p []byte) (int, error)

Write encodes data into base64 encoding and writes it into the big string

type Error

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

Error keeps position and context of an error and can pretty print it It supports 3 forms of formatting:

%v - produces output:
parse error at pos 15: expected object key

%#v
parse error at pos 15: expected object key `{"some":"text",123}`

%+v
parse error at pos 15: expected object key
{"some":"text",123}
_______________^___

It also supports width setting which changes max size of context shown in extended forms

func NewError

func NewError(b []byte, ref, i int, e error) Error

NewError creates new error

func (Error) Cause added in v0.2.0

func (e Error) Cause() error

Cause returns cause error

func (Error) Error

func (e Error) Error() string

func (Error) Format

func (e Error) Format(s fmt.State, c rune)

func (Error) Pos

func (e Error) Pos() int

Pos returns stream position at which error has happened

func (Error) Unwrap added in v0.2.0

func (e Error) Unwrap() error

type Num added in v0.2.0

type Num []byte

func (Num) Float64 added in v0.2.0

func (buf Num) Float64() (float64, error)

func (Num) Int64 added in v0.2.0

func (buf Num) Int64() (int64, error)

func (Num) String added in v0.2.0

func (buf Num) String() string

func (Num) Uint64 added in v0.2.0

func (buf Num) Uint64() (uint64, error)

type Reader added in v0.2.0

type Reader struct {
	// contains filtered or unexported fields
}

Reader parses byte stream and allows you to manipulate structured data

Example

This example shows how you can collect some info out of json without fully parsing it

data := `[{"user":"Kyle","visits":134},{"user":"Rose","visits":45,"online":true},{"user":"Adam","visits":37}]`
r := WrapString(data)

var visits int
for r.HasNext() {
	r.Search("visits")
	visits += r.MustInt()
	r.GoOut(1) // number of keys passed to Search()
}

fmt.Printf("Total visits: %d\n", visits)
Output:
Total visits: 216
Example (Messenger)
data := `
		{"status":"online","term":3,"messages":[
			{"text":"Tomorrow at 6pm Remember!!","sender":"Violet","term":3}]}
		{"status":"online","term":3,"messages":[
			{"text":"Tomorrow at 6pm Remember!!","sender":"Violet","term":3}]}
		{"status":"online","term":4,"messages":[
			{"text":"Tomorrow at 6pm Remember!!","sender":"Violet","term":3},
			{"text":"Hi, Nik! You won't believe who have I met just now!","sender":"Dan","term":4},
			{"text":"When did you see Dan last time?","sender":"Jane","term":4},
		]}
	`
// Let's imagine that `stream` is a socket and we are taking server updates once a 5 seconds
stream := strings.NewReader(data)

type Message struct {
	Text   string
	Sender string
	Term   int
}

r := NewReader(stream)

messagesTotal := 0
lastTerm := 0
for r.Type() != None {
	ok := r.Inspect(func(r *Reader) bool {
		term, err := r.Int()
		if err != nil {
			return false
		}
		res := term != lastTerm
		lastTerm = term
		return res
	}, "term")

	if !ok {
		continue
	}

	r.Inspect(func(r *Reader) bool {
		for r.HasNext() {
			var m Message
			/*
				err := r.Unmarshal(&m)
				if err != nil {
					return false
				}
			*/

			for r.HasNext() {
				k := string(r.NextString())
				switch k {
				case "text":
					m.Text = string(r.NextString())
				case "sender":
					m.Sender = string(r.NextString())
				case "term":
					v, err := json.Number(r.NextNumber()).Int64()
					if err != nil {
						return false
					}
					m.Term = int(v)
				default:
					r.Skip()
				}
			}

			if m.Term != lastTerm {
				continue
			}

			messagesTotal++

			fmt.Printf("Message from %v: \t%v\n", m.Sender, m.Text)
		}
		return false
	}, "messages")
}
if err := r.Err(); err != io.EOF && err != nil {
	// process eny errors at one place
}

fmt.Printf("Connection closed. Total messages got: %d", messagesTotal)
Output:
Message from Violet: 	Tomorrow at 6pm Remember!!
Message from Dan: 	Hi, Nik! You won't believe who have I met just now!
Message from Jane: 	When did you see Dan last time?
Connection closed. Total messages got: 3

func NewReader added in v0.2.0

func NewReader(r io.Reader) *Reader

NewReader reads stream

func NewReaderBufferSize added in v0.2.0

func NewReaderBufferSize(r io.Reader, s int) *Reader

NewReaderBufferSize creates reader from stream with specified buffer size

func Search(data []byte, keys ...interface{}) *Reader

Search is a shortcut for Wrap(data).Search(keys...)

func Wrap

func Wrap(b []byte) *Reader

Wrap wraps single byte buffer info json reader object

func WrapString

func WrapString(s string) *Reader

WrapString wraps string

func (*Reader) Base64Reader added in v0.2.0

func (r *Reader) Base64Reader(enc *base64.Encoding) Base64Reader

Base64Reader allows to read raw bytes stream encoded in base64 encoding

Example
var buf bytes.Buffer

token := make([]byte, 10)
_, _ = rand.New(rand.NewSource(1)).Read(token)

// Writer

fmt.Printf("Writer sends message with a raw bytes token:   % 2x\n", token)

// create json writer on top of io.Writer
w := NewStreamWriter(&buf)

// generate object
w.ObjStart()

w.ObjKey([]byte("user"))
w.String([]byte("nikandfor"))

w.ObjKey([]byte("token"))
// this way we can handle json strings of any size with constant memory usage
sw := w.Base64Writer(base64.RawStdEncoding)
_, _ = sw.Write(token) // we don't care errors here, we'll care them later at once
_ = sw.Close()

w.ObjEnd()

// check all errors until now
err := w.Flush()
if err != nil {
	// process
}

// Reader

// read io.Reader stream on the fly with constant memory usage
r := NewReader(&buf)

var user string
var tokenDec []byte = make([]byte, len(token))

// check if it's really object (but we can skip this)
if tp := r.Type(); tp != Object {
	fmt.Printf("Expected object, got %v\n", tp)
}

// read all key-value pairs
for r.HasNext() {
	// key
	k := r.NextString()

	// value
	switch {
	case bytes.Equal(k, []byte("user")):
		user = string(r.NextString())
	case bytes.Equal(k, []byte("token")):
		sr := r.Base64Reader(base64.RawStdEncoding)
		_, err := io.ReadFull(sr, tokenDec)
		if err != nil {
			// will be io.EOF if there is less that len(tokenDec) bytes at string
		}
		sr.Close()
	default:
		r.Skip()
	}
}

// check error just before application logic
err = r.Err()
if err != nil {
	// process
}

fmt.Printf("Reader got message from user: %v, token % 2x\n", user, tokenDec)
Output:
Writer sends message with a raw bytes token:   52 fd fc 07 21 82 65 4f 16 3f
Reader got message from user: nikandfor, token 52 fd fc 07 21 82 65 4f 16 3f

func (*Reader) Bool added in v0.2.0

func (r *Reader) Bool() (bool, error)

func (*Reader) CheckString added in v0.2.0

func (r *Reader) CheckString() (string, error)

func (*Reader) CompareKey added in v0.2.0

func (r *Reader) CompareKey(k []byte) bool

CompareKey compares given key weth the next string value. It doesn't decodes escape sequences and doesn't check for utf8 correctness

func (*Reader) Err added in v0.2.0

func (r *Reader) Err() error

Err returns first happened error. It returns Error type that could nicely format error message for you if you will see it at console or with monospace font

func (*Reader) ErrorHere added in v0.2.0

func (r *Reader) ErrorHere(err error) error

func (*Reader) Float64 added in v0.2.0

func (r *Reader) Float64() (float64, error)

func (*Reader) GoOut added in v0.2.0

func (r *Reader) GoOut(d int)

GoOut goes out of object or array (reads until end). d means number of objects you want to out of. It's the ideal pair for (*Reader).Search method (See (*Reader).Inspect source code as an example)

func (*Reader) HasNext added in v0.2.0

func (r *Reader) HasNext() (res bool)

HasNext checks if you can read next element at the current object (or array). It also reads opening and closing brakets internally

It moves position inside object (or array), so you'll get different result if you call it twice in a row. For example you have the following json

{"a": [{"b": "c"}, {"d": "e"}]}
    ^ - cursor is here (you've just read "a" and want to iterate over it's value)
// first time you call HasNext it returns true and moves cursor
{"a": [{"b": "c"}, {"d": "e"}]}
       ^ - here
// second time you call HasNext it returns true and moves cursor
{"a": [{"b": "c"}, {"d": "e"}]}
        ^ - here (inside b->c object)
// all subsequent calls will not move cursor anywhere until you read "b" key

See example how to use it properly

Example
data := `{"a": [{"b": "c"}, {"d": "e"}],"f": true}`
r := WrapString(data)

r.Search("a")

for r.HasNext() { // over array
	for r.HasNext() { // over array elements key-value pairs
		// we must use for loop here even if we know it's the only pair inside
		// because we have to read closing braket
		key := r.NextString()
		val := r.NextString()
		fmt.Printf("pair:  %q -> %q\n", key, val)
	}
}

fKey := r.NextString()
switch r.Type() {
case Bool:
	fVal, _ := r.Bool() // error could be here if there is truu instead of true for example
	fmt.Printf("%q -> %v\n", fKey, fVal)
}

r.GoOut(1) // to get out of the most outer object
// it's the pair call to the first Search and we always have to call it if we want to read following data correctly

fmt.Printf("end of buffer, next value type: %v\n", r.Type())
Output:
pair:  "b" -> "c"
pair:  "d" -> "e"
"f" -> true
end of buffer, next value type: None

func (*Reader) Inspect added in v0.2.0

func (r *Reader) Inspect(cb func(r *Reader) bool, ks ...interface{}) bool

Inspect inspects requested value and returns back or skips object according to callback returned value: true - return back, false - skip the object. Whole data from the beginning of the object until inspected value (including) is stored in memory and buffer doesn't shrunk back

func (*Reader) Int added in v0.2.0

func (r *Reader) Int() (int, error)

func (*Reader) Int64 added in v0.2.0

func (r *Reader) Int64() (int64, error)

func (*Reader) IsNull added in v0.2.0

func (r *Reader) IsNull() bool

func (*Reader) Lock added in v0.2.0

func (r *Reader) Lock()

Lock locks all data since the current position and prevents them from freeing. So if buffer has ended it would be grown to accommodate new data You should use it for inspecting some small part of data and decide will you parse them from the beginning or just unlock reader and will go further.

See (*Reader).Inspect() source code for better understanding.

func (*Reader) MustCheckString added in v0.2.0

func (r *Reader) MustCheckString() string

func (*Reader) MustInt added in v0.2.0

func (r *Reader) MustInt() int

func (*Reader) MustInt64 added in v0.2.0

func (r *Reader) MustInt64() int64

func (*Reader) MustUint added in v0.2.0

func (r *Reader) MustUint() uint

func (*Reader) MustUint64 added in v0.2.0

func (r *Reader) MustUint64() uint64

func (*Reader) NextAsBytes added in v0.2.0

func (r *Reader) NextAsBytes() []byte

NextAsBytes reads next object of any type and returns it as a raw byte slice without decoding (including string quotes)

func (*Reader) NextNumber added in v0.2.0

func (r *Reader) NextNumber() []byte

NextNumber reads next number (possible incorrect) of any size and returns it as bytes It doesn't check if nubmer is correct. It could have two points or '+' and '-' signs in the middle of string

func (*Reader) NextString added in v0.2.0

func (r *Reader) NextString() (res []byte)

NextString read next object key or string checking utf8 encoding correctness and decoding '\t', '\n', '\r' escape sequences

func (*Reader) Reset added in v0.2.0

func (r *Reader) Reset(b []byte) *Reader

Reset resets the reader

func (*Reader) ResetErr added in v0.2.0

func (r *Reader) ResetErr()

func (*Reader) ResetReader added in v0.2.0

func (r *Reader) ResetReader(rd io.Reader) *Reader

ResetReader resets the reader it reuses buffer (if you created reader by Wrap for example) if you used Lock and read a big object into the memory it doesn't shrink buffer back

func (*Reader) ResetString added in v0.2.0

func (r *Reader) ResetString(s string) *Reader

ResetString resets the reader

func (*Reader) Return added in v0.2.0

func (r *Reader) Return()

Return returns the reading position to the previously locked by (*Reader).Lock

func (*Reader) Search added in v0.2.0

func (r *Reader) Search(keys ...interface{}) *Reader

Search searches for value at the specified path. It supports keys of type int for arrays and string or []byte for objects

Example
data := `
	{"day": "Mon", "stats": {"views": {"by_partofday": [1, 2, 3]}}}
	{"day": "Tue", "stats": {"views": {"by_partofday": [3, 2, 3]}}}
	{"day": "Wed", "stats": {"views": {"by_partofday": [4, 1, 5]}}}
	{"day": "Thu", "stats": {"views": {"by_partofday": [1, 2, 2]}}}
	`

s := strings.NewReader(data)

r := NewReader(s)

sum := 0
days := 0
for r.Type() != None {
	days++
	r.Search("stats", "views", "by_partofday") // goes inside to requested value
	for r.HasNext() {
		sum += r.MustInt()
	}
	r.GoOut(3) // goes back up at 3 levels (to the end of the current day object)
}

fmt.Printf("total views for %v days: %v\n", days, sum)
Output:
total views for 4 days: 29

func (*Reader) Skip added in v0.2.0

func (r *Reader) Skip()

Skip reads and skips one next value. It doesn't metter if you call it before ':' or just at start of value (it actually doesn't matter if ':' or ',' were or not at all)

func (*Reader) StringOrEmpty added in v0.2.0

func (r *Reader) StringOrEmpty() string

func (*Reader) StringReader added in v0.2.0

func (r *Reader) StringReader() StringReader

StringReader allows to read string value as an io.ReadCloser interface

func (*Reader) Type added in v0.2.0

func (r *Reader) Type() Type

Type returns type of the next value. It skips commas ',' and colons ':' silently It doesn't skip or read over other tokens even if called multiple times

Example
data := `{"first": "string", "second": 123, "third": [1.1, 3.3, 7.7], "fourth": {"again": "string", "and": {"object": "here"}}}`
var parse func(*Reader, int)
parse = func(r *Reader, d int) {
	switch tp := r.Type(); tp {
	case String, Number, Bool, Null:
		// read by one of accordingly
		//   r.CheckString()
		//   r.Int() or r.Float64()
		//   r.Bool()
		//   r.Skip() // you can skip any value like this
		val := r.NextAsBytes() // reads any value including object and array as raw bytes
		fmt.Printf("%*s is %v\n", d*4, val, tp)
	case Array:
		for r.HasNext() {
			parse(r, d+1)
		}
	case Object:
		for r.HasNext() {
			key := r.NextString()
			fmt.Printf("%*s ->\n", d*4, key)
			parse(r, d+1)
		}
	default:
		err := r.Err()
		fmt.Printf("%*s got type %v err %#v\n", d*4, "", tp, err)
	}
}

r := WrapString(data)

parse(r, 1)
if err := r.Err(); err != nil {
	fmt.Printf("reader: %+40v", err)
}
Output:
first ->
"string" is String
second ->
     123 is Number
third ->
         1.1 is Number
         3.3 is Number
         7.7 is Number
fourth ->
   again ->
    "string" is String
     and ->
      object ->
          "here" is String

func (*Reader) Uint added in v0.2.0

func (r *Reader) Uint() (uint, error)

func (*Reader) Uint64 added in v0.2.0

func (r *Reader) Uint64() (uint64, error)

func (*Reader) Unlock added in v0.2.0

func (r *Reader) Unlock()

Unlock unlocks previously locked reader by (*Reader).Lock(). It doesn't change reader position

type StringReader added in v0.2.0

type StringReader struct {
	*Reader
}

StringReader allows to read string value as an io.ReadCloser interface

func (StringReader) Close added in v0.2.0

func (r StringReader) Close() error

Close must be called to finish reading current string value It will skip unread part of string if any

func (StringReader) Read added in v0.2.0

func (r StringReader) Read(p []byte) (int, error)

Read reads data from a big string value It returns io.EOF if entire string has been read

type StringWriter added in v0.2.0

type StringWriter struct {
	*Writer
}

StringWriter allows to write string value as an io.WriteCloser interface

func (StringWriter) Close added in v0.2.0

func (w StringWriter) Close() error

Close must be called to finish the string

func (StringWriter) Write added in v0.2.0

func (w StringWriter) Write(p []byte) (int, error)

Write writes data into the big string

type Type

type Type byte

Type is a token type

const (
	None   Type = 0
	Null   Type = 'n'
	Bool   Type = 'b'
	Array  Type = '['
	Object Type = '{'
	String Type = 's'
	Number Type = 'N'
)

func (Type) String

func (i Type) String() string

type Writer added in v0.2.0

type Writer struct {
	// contains filtered or unexported fields
}

func NewIndentWriter added in v0.2.0

func NewIndentWriter(b []byte, p, i []byte) *Writer

func NewStreamWriter added in v0.2.0

func NewStreamWriter(w io.Writer) *Writer

func NewStreamWriterBuffer added in v0.2.0

func NewStreamWriterBuffer(w io.Writer, b []byte) *Writer

func NewWriter added in v0.2.0

func NewWriter(b []byte) *Writer

func (*Writer) ArrayEnd added in v0.2.0

func (w *Writer) ArrayEnd()

func (*Writer) ArrayStart added in v0.2.0

func (w *Writer) ArrayStart()

func (*Writer) Base64Writer added in v0.2.0

func (w *Writer) Base64Writer(enc *base64.Encoding) Base64Writer

Base64Writer allows to write raw bytes stream as an base64 encoded string

func (*Writer) Bool added in v0.2.0

func (w *Writer) Bool(f bool)

func (*Writer) Bytes added in v0.2.0

func (w *Writer) Bytes() []byte

func (*Writer) Err added in v0.2.0

func (w *Writer) Err() error

func (*Writer) Flush added in v0.2.0

func (w *Writer) Flush() error

func (*Writer) NewLine added in v0.2.0

func (w *Writer) NewLine()

func (*Writer) Null added in v0.2.0

func (w *Writer) Null()

func (*Writer) Number added in v0.2.0

func (w *Writer) Number(v []byte)

func (*Writer) ObjEnd added in v0.2.0

func (w *Writer) ObjEnd()

func (*Writer) ObjKey added in v0.2.0

func (w *Writer) ObjKey(k []byte)

func (*Writer) ObjKeyString added in v0.2.0

func (w *Writer) ObjKeyString(k string)

func (*Writer) ObjStart added in v0.2.0

func (w *Writer) ObjStart()

func (*Writer) RawBytes added in v0.2.0

func (w *Writer) RawBytes(v []byte)

func (*Writer) RawString added in v0.2.0

func (w *Writer) RawString(v []byte)

func (*Writer) RawStringString added in v0.2.0

func (w *Writer) RawStringString(v string)

func (*Writer) SetIndent added in v0.2.0

func (w *Writer) SetIndent(pref, ind []byte)

func (*Writer) String added in v0.2.0

func (w *Writer) String(v []byte)

func (*Writer) StringString added in v0.2.0

func (w *Writer) StringString(v string)

func (*Writer) StringWriter added in v0.2.0

func (w *Writer) StringWriter() StringWriter

StringWriter allows to write string value as an io.WriteCloser interface

func (*Writer) Write added in v0.2.0

func (w *Writer) Write(p []byte) (int, error)

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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