|
| 1 | +# jsonc - JSON with comments for Go |
| 2 | + |
| 3 | +[](http://godoc.org/github.com/marcozac/go-jsonc) |
| 4 | + |
| 5 | +[](https://github.com/marcozac/go-jsonc/actions/workflows/ci.yml) |
| 6 | +[](https://codecov.io/gh/marcozac/go-jsonc) |
| 7 | +[](https://goreportcard.com/report/github.com/marcozac/go-jsonc) |
| 8 | + |
| 9 | +`jsonc` is a light and dependency-free package for working with JSON with comments data built on top of `encoding/json`. |
| 10 | +It allows to remove comments converting to valid JSON-encoded data and to unmarshal JSON with comments into Go values. |
| 11 | + |
| 12 | +The dependencies listed in [go.mod](/go.mod) are only used for testing and benchmarking or to support [alternative libraries](#alternative-libraries). |
| 13 | + |
| 14 | +## Features |
| 15 | + |
| 16 | +- Full support for comment lines and block comments |
| 17 | +- Preserve the content of strings that contain comment characters |
| 18 | +- Sanitize JSON with comments data by removing comments |
| 19 | +- Unmarshal JSON with comments into Go values |
| 20 | + |
| 21 | +## Installation |
| 22 | + |
| 23 | +Install the `jsonc` package: |
| 24 | + |
| 25 | +```bash |
| 26 | +go get github.com/marcozac/go-jsonc |
| 27 | +``` |
| 28 | + |
| 29 | +## Usage |
| 30 | + |
| 31 | +### Sanitize - Remove comments from JSON data |
| 32 | + |
| 33 | +`Sanitize` removes all comments from JSON data, returning valid JSON-encoded byte slice that is compatible with standard library's json.Unmarshal. |
| 34 | + |
| 35 | +It works with comment lines and block comments anywhere in the JSONC data, preserving the content of strings that contain comment characters. |
| 36 | + |
| 37 | +#### Example |
| 38 | + |
| 39 | +```go |
| 40 | +package main |
| 41 | + |
| 42 | +import ( |
| 43 | + "encoding/json" |
| 44 | + |
| 45 | + "github.com/marcozac/go-jsonc" |
| 46 | +) |
| 47 | + |
| 48 | +func main() { |
| 49 | + invalidData := []byte(`{ |
| 50 | + // a comment |
| 51 | + "foo": "bar" /* a comment in a weird place */, |
| 52 | + /* |
| 53 | + a block comment |
| 54 | + */ |
| 55 | + "hello": "world" // another comment |
| 56 | + }`) |
| 57 | + |
| 58 | + // Remove comments from JSONC |
| 59 | + data, err := jsonc.Sanitize(invalidData) |
| 60 | + if err != nil { |
| 61 | + ... |
| 62 | + } |
| 63 | + |
| 64 | + var v struct{ |
| 65 | + Foo string |
| 66 | + Hello string |
| 67 | + } |
| 68 | + |
| 69 | + // Unmarshal using any other library |
| 70 | + if err := json.Unmarshal(data, &v); err != nil { |
| 71 | + ... |
| 72 | + } |
| 73 | +} |
| 74 | +``` |
| 75 | + |
| 76 | +### Unmarshal - Parse JSON with comments into a Go value |
| 77 | + |
| 78 | +`Unmarshal` replicates the behavior of the standard library's json.Unmarshal function, with the addition of support for comments. |
| 79 | + |
| 80 | +It is optimized to avoid calling [`Sanitize`](#sanitize---remove-comments-from-json-data) unless it detects comments in the data. |
| 81 | +This avoids the overhead of removing comments when they are not present, improving performance on small data sets. |
| 82 | + |
| 83 | +It first checks if the data contains comment characters as `//` or `/*` using [`HasCommentRunes`](https://pkg.go.dev/github.com/marcozac/go-jsonc#HasCommentRunes). |
| 84 | +If no comment characters are found, it directly unmarshals the data. |
| 85 | + |
| 86 | +Only if comments are detected it calls [`Sanitize`](#sanitize---remove-comments-from-json-data) before unmarshaling to remove them. |
| 87 | +So, `Unmarshal` tries to skip unnecessary work when possible, but currently it is not possible to detect false positives as `//` or `/*` inside strings. |
| 88 | + |
| 89 | +Since the comment detection is based on a simple rune check, it is not recommended to use `Unmarshal` on large data sets unless you are not sure whether they contain comments. |
| 90 | +Indeed, `HasCommentRunes` needs to checks every single byte before to return `false` and may drastically slow down the process. |
| 91 | + |
| 92 | +In this case, it is more efficient to call [`Sanitize`](#sanitize---remove-comments-from-json-data) before to unmarshal the data. |
| 93 | + |
| 94 | +#### Example |
| 95 | + |
| 96 | +```go |
| 97 | +package main |
| 98 | + |
| 99 | +import "github.com/marcozac/go-jsonc" |
| 100 | + |
| 101 | +func main() { |
| 102 | + invalidData := []byte(`{ |
| 103 | + // a comment |
| 104 | + "foo": "bar" |
| 105 | + }`) |
| 106 | + |
| 107 | + var v struct{ Foo string } |
| 108 | + |
| 109 | + err := jsonc.Unmarshal(invalidData, &v) |
| 110 | + if err != nil { |
| 111 | + ... |
| 112 | + } |
| 113 | +} |
| 114 | +``` |
| 115 | + |
| 116 | +## Alternative libraries |
| 117 | + |
| 118 | +By default, `jsonc` uses the standard library's `encoding/json` to unmarshal JSON data and has no external dependencies. |
| 119 | + |
| 120 | +It is possible to use build tags to use alternative libraries instead of the standard library's `encoding/json`: |
| 121 | + |
| 122 | +| Tag | Library | |
| 123 | +| ------------ | -------------------------------------------------------------------- | |
| 124 | +| none or both | standard library | |
| 125 | +| jsoniter | [`github.com/json-iterator/go`](https://github.com/json-iterator/go) | |
| 126 | +| go_json | [`github.com/goccy/go-json`](https://github.com/goccy/go-json) | |
| 127 | + |
| 128 | +## Benchmarks |
| 129 | + |
| 130 | +This library aims to have performance comparable to the standard library's `encoding/json`. |
| 131 | +Unfortunately, comments removal is not free and it is not possible to avoid the overhead of removing comments when they are present. |
| 132 | + |
| 133 | +Currently `jsonc` performs worse than the standard library's `encoding/json` on small data sets about 27% on data with comments in strings and 16% on data without comments. |
| 134 | +On medium data sets, the performance gap is increased to about 30% on data with comments in strings and reduced to 12% on data without comments. |
| 135 | + |
| 136 | +However, using one of the [alternative libraries](#alternative-libraries), it is possible to achieve better performance than the standard library's `encoding/json` even considering the overhead of removing comments. |
| 137 | + |
| 138 | +See [benchmarks](/benchmarks) for the full results. |
| 139 | + |
| 140 | +The benchmarks are run on a MacBook Pro (16-inch, 2021), Apple M1 Max, 32 GB RAM. |
| 141 | + |
| 142 | +## Contributing |
| 143 | + |
| 144 | +:heart: Contributions are ~~needed~~ welcome! |
| 145 | + |
| 146 | +Please open an issue or submit a pull request if you would like to contribute. |
| 147 | + |
| 148 | +To submit a pull request: |
| 149 | + |
| 150 | +- Fork this repository |
| 151 | +- Create a new branch |
| 152 | +- Make changes and commit |
| 153 | +- Push to your fork and submit a pull request |
| 154 | + |
| 155 | +## License |
| 156 | + |
| 157 | +This project is licensed under the Apache 2.0 license. See [LICENSE](/LICENSE) file for details. |
0 commit comments