github.com/zerosnake0/jzon@v0.0.9-0.20230801092939-1b135cb83f7f/README.md (about)

     1  [![Go Report Card](https://goreportcard.com/badge/github.com/zerosnake0/jzon)](https://goreportcard.com/report/github.com/zerosnake0/jzon)
     2  [![PkgGoDev](https://pkg.go.dev/badge/github.com/zerosnake0/jzon)](https://pkg.go.dev/github.com/zerosnake0/jzon)
     3  [![Github Workflow](https://github.com/zerosnake0/jzon/workflows/Test/badge.svg)](https://github.com/zerosnake0/jzon/actions?query=workflow%3ATest)
     4  [![Build Status](https://travis-ci.org/zerosnake0/jzon.svg?branch=master)](https://travis-ci.org/zerosnake0/jzon)
     5  [![codecov](https://codecov.io/gh/zerosnake0/jzon/branch/master/graph/badge.svg)](https://codecov.io/gh/zerosnake0/jzon)
     6  [![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://github.com/pre-commit/pre-commit)
     7  
     8  # jzon
     9  
    10  A high performance json library for Golang
    11  
    12  ## Why another jsoniter?
    13  
    14  The code I write here is very similar to [github.com/json-iterator/go](https://github.com/json-iterator/go),
    15  so you may ask why reinvent the wheel.
    16  
    17  For sure that I benefit a lot from the `jsoniter` library, but i found some inconvenience for me to use it
    18  in some condition, for example:
    19  
    20  - the iterator methods ReadString accepts null, there is no method which accepts exactly string.
    21    I have to do some extra check before calling.
    22  - some behavior is not compatible with the standard library.
    23  - I want a chained streamer
    24  
    25  On the other hand, I also want to learn how the `jsoniter` works, so there is this repo.
    26  
    27  ## What's different from jsoniter?
    28  
    29  Here are some of the differences:
    30  
    31  - the iterator methods accept the exact type, for example ReadString accepts only string, not null
    32  - the behavior is almost the same as the standard library (when an error returns, the behavior may differ
    33    from the standard library)
    34  - the error of the iterator is returned instead of being saved inside iterator
    35  - the decoder/encoder interface has additional options, like struct tag options
    36  
    37  Some features of `jsoniter` are not implemented, and may be not implemented in the future neither.
    38  I choose only the ones I need to implement.
    39  
    40  ## Compatibility with standard library
    41  
    42  I tried implemented a version which is completely compatible with the standard library:
    43  
    44  https://github.com/zerosnake0/jzon/tree/reflect
    45  
    46  The benchmark shows that it's much faster than the standard library.
    47  However it is still much slower than the current version,
    48  which cannot be exactly the same as standard library (at least in my POV).
    49  
    50  The major incompatibility is about the two following interfaces:
    51  - `json.Marshaler`
    52  - `encoding.TextMarshaler`
    53  
    54  The method on pointer receiver may be called with an unaddressable value,
    55  for example:
    56  
    57  ```go
    58  type field struct {}
    59  
    60  func (*field) MarshalJSON() ([]byte, error)
    61  
    62  type st struct {
    63      F field
    64  }
    65  
    66  json.Marshal(st{}) // will not call field.MarshalJSON
    67  jzon.Marshal(st{}) // will call field.MarshalJSON
    68  ```
    69  
    70  So the user should be care when marshaling a value when method on
    71  pointer receiver is involved
    72  
    73  You can check the tests for more detailed info about the difference
    74  
    75  ## How to use
    76  
    77  ### Standard library like
    78  
    79  ```go
    80  import "github.com/zerosnake0/jzon"
    81  
    82  // Unmarshal
    83  err := jzon.Unmarshal(b, &data)
    84  
    85  // Marshal
    86  b, err := jzon.Marshal(&data)
    87  
    88  // Decoder
    89  dec := jzon.NewDecoder(reader)
    90  defer dec.Release()
    91  err := dec.Decode(&data)
    92  
    93  // Encoder
    94  enc := jzon.NewEncoder(writer)
    95  defer enc.Release()
    96  err := enc.Encode(&data)
    97  ```
    98  
    99  ### Iterator
   100  
   101  ```go
   102  iter := jzon.NewIterator()
   103  defer iter.Release()
   104  iter.Reset(b)
   105  jzon.ReadVal(&data)
   106  ```
   107  
   108  ### Streamer
   109  
   110  ```go
   111  var w io.Writer
   112  
   113  streamer := jzon.NewStreamer()
   114  defer streamer.Release()
   115  streamer.Reset(w)
   116  streamer.Value(&data)
   117  streamer.Flush()
   118  ```
   119  
   120  ### Custom Decoder
   121  
   122  see `decoder_test.go`
   123  
   124  ```go
   125  type testIntDecoder struct{}
   126  
   127  func (*testIntDecoder) Decode(ptr unsafe.Pointer, it *Iterator, opts *DecOpts) error {
   128      ...
   129  }
   130  
   131  dec := NewDecoderConfig(&DecoderOption{
   132      ValDecoders: map[reflect.Type]ValDecoder{
   133          reflect.TypeOf(int(0)): (*testIntDecoder)(nil),
   134      },
   135      CaseSensitive: true,
   136  })
   137  
   138  // standard library like
   139  err := dec.Unmarshal(b, &data)
   140  
   141  // iterator
   142  iter := dec.NewIterator()
   143  defer iter.Release()
   144  ```
   145  
   146  ### Custom Encoder
   147  
   148  see `encoder_test.go`
   149  
   150  ```go
   151  type testIntEncoder struct{}
   152  
   153  func (*testIntEncoder) IsEmpty(ptr unsafe.Pointer) bool {
   154      ...
   155  }
   156  
   157  func (*testIntEncoder) Encode(ptr unsafe.Pointer, s *Streamer, opts *EncOpts) {
   158      ...
   159  }
   160  
   161  enc := NewEncoderConfig(&EncoderOption{
   162      ValEncoders: map[reflect.Type]ValEncoder{
   163          reflect.TypeOf(int(0)): (*testIntEncoder)(nil),
   164      },
   165  })
   166  
   167  // standard library like
   168  b, err := enc.Marshal(&data)
   169  
   170  // streamer
   171  streamer := enc.NewStreamer()
   172  defer streamer.Release()
   173  ```