Skip to content

Commit 9844e1d

Browse files
committed
docs: add README
* test: skip redundant benchmarks
1 parent 08e9f3d commit 9844e1d

File tree

10 files changed

+620
-25
lines changed

10 files changed

+620
-25
lines changed

README.md

Lines changed: 92 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,40 @@
11
# jsonc - JSON with comments for Go
22

3-
## Description
3+
[![Go Doc](https://img.shields.io/badge/godoc-reference-blue.svg)](http://godoc.org/github.com/marcozac/go-jsonc)
4+
![License](https://img.shields.io/github/license/marcozac/go-jsonc?color=blue)
5+
[![CI](https://github.com/marcozac/go-jsonc/actions/workflows/ci.yml/badge.svg)](https://github.com/marcozac/go-jsonc/actions/workflows/ci.yml)
6+
[![codecov](https://codecov.io/gh/marcozac/go-jsonc/branch/main/graph/badge.svg?token=JYj7gCZauN)](https://codecov.io/gh/marcozac/go-jsonc)
7+
[![Go Report Card](https://goreportcard.com/badge/github.com/marcozac/go-jsonc)](https://goreportcard.com/report/github.com/marcozac/go-jsonc)
48

5-
jsonc is a Go package for working with JSONC files.
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.
611

7-
JSONC is an extension to JSON that allows comments. This package provides functions to:
12+
The dependencies listed in [go.mod](/go.mod) are only used for testing and benchmarking or to support [alternative libraries](#alternative-libraries).
813

9-
- Sanitize JSONC data by removing comments
10-
- Unmarshal JSONC into Go values
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
1120

1221
## Installation
1322

14-
Install the jsonc package:
23+
Install the `jsonc` package:
1524

16-
`go get github.com/marcozac/go-jsonc`
25+
```bash
26+
go get github.com/marcozac/go-jsonc
27+
```
1728

1829
## Usage
1930

20-
### Sanitize JSON with comments
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.
2136

22-
Remove comments making the data compatible to JSON.
37+
#### Example
2338

2439
```go
2540
package main
@@ -33,7 +48,11 @@ import (
3348
func main() {
3449
invalidData := []byte(`{
3550
// a comment
36-
"foo": "bar"
51+
"foo": "bar" /* a comment in a weird place */,
52+
/*
53+
a block comment
54+
*/
55+
"hello": "world" // another comment
3756
}`)
3857

3958
// Remove comments from JSONC
@@ -42,19 +61,37 @@ func main() {
4261
...
4362
}
4463

45-
var v struct{ Foo string }
64+
var v struct{
65+
Foo string
66+
Hello string
67+
}
4668

4769
// Unmarshal using any other library
48-
err = json.Unmarshal(data, &v)
49-
if err != nil {
70+
if err := json.Unmarshal(data, &v); err != nil {
5071
...
5172
}
5273
}
5374
```
5475

55-
### Unmarshal JSON with comments data into a Go value
76+
### Unmarshal - Parse JSON with comments into a Go value
5677

57-
The Unmarshal function takes JSONC data and a pointer to the target Go value to unmarshal into.
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
5895

5996
```go
6097
package main
@@ -76,6 +113,45 @@ func main() {
76113
}
77114
```
78115

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+
79155
## License
80156

81-
This project is licensed under the Apache 2.0 license. See LICENSE file for details.
157+
This project is licensed under the Apache 2.0 license. See [LICENSE](/LICENSE) file for details.

benchmarks/README.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Benchmark results
2+
3+
The tables below show the performance of [`Unmarshal`](#unmarshal---parse-json-with-comments-into-a-go-value) compared to the standard library's `encoding/json` and other alternative libraries on small and medium data sets.
4+
5+
They are formatted as follows:
6+
7+
| Data set | s/op | B/op | allocs/op |
8+
| ------------- | ------------------------------------------- | ---- | --------- |
9+
| Set reference | result (Δ% on reference / reference result) | same | same |
10+
11+
See the files in this directory for the full report.
12+
13+
### Standard library
14+
15+
The tables below show the performance of [`Unmarshal`](#unmarshal---parse-json-with-comments-into-a-go-value) compared to the standard library's `encoding/json` on small and medium data sets.
16+
17+
| **Small data set** | s/op | B/op | allocs/op |
18+
| -------------------------------------------------------------------------------------- | ------------------------- | --------------------------- | ---------------------- |
19+
| [With comments](../testdata/small.json) | 2.536µ | 1.344Ki | 22.00 |
20+
| [Without comments](../testdata/small_uncommented.json) (comment characters in strings) | 2.425µ (+27.17% / 1.907µ) | 1.219Ki (+14.71% / 1.062Ki) | 22.00 (+4.76% / 21.00) |
21+
| [Without comment characters](../testdata/small_no_comment_runes.json) | 2.306µ (+16.11% / 1.986µ) | 1.062Ki (~% / 1.062Ki) | 21.00 (~% / 21.00) |
22+
23+
| **Medium data set** | s/op | B/op | allocs/op |
24+
| -------------------------------------------------------------------------------------- | ------------------------- | --------------------------- | ------------------------ |
25+
| [With comments](../testdata/small.json) | 301.2µ | 324.7Ki | 1.067k |
26+
| [Without comments](../testdata/small_uncommented.json) (comment characters in strings) | 202.3µ (+30.86% / 154.6µ) | 148.7Ki (+60.41% / 92.70Ki) | 1.067k (+0.09% / 1.066k) |
27+
| [Without comment characters](../testdata/small_no_comment_runes.json) | 170.6µ (+11.63% / 152.8µ) | 92.70Ki (~% / 92.70Ki) | 1.066k (~% / 1.066k) |
28+
29+
### With [`github.com/json-iterator/go`](https://github.com/json-iterator/go)
30+
31+
| **Small data set** | s/op | B/op | allocs/op |
32+
| -------------------------------------------------------------------------------------- | ------------------------- | ----------------------- | ---------------------- |
33+
| [With comments](../testdata/small.json) | 1.632µ | 944.0 | 14.00 |
34+
| [Without comments](../testdata/small_uncommented.json) (comment characters in strings) | 1.702µ (+11.94% / 1.521µ) | 816.0 (+24.39% / 656.0) | 14.00 (+7.69% / 13.00) |
35+
| [Without comment characters](../testdata/small_no_comment_runes.json) | 1.603µ (~% / 1.598µ) | 656.0 (~% / 656.0) | 12.00 (~% / 13.00) |
36+
37+
| **Medium data set** | s/op | B/op | allocs/op |
38+
| -------------------------------------------------------------------------------------- | ------------------------- | --------------------------- | ------------------------ |
39+
| [With comments](../testdata/small.json) | 245.0µ | 407.8Ki | 3.484k |
40+
| [Without comments](../testdata/small_uncommented.json) (comment characters in strings) | 142.4µ (+42.25% / 100.1µ) | 231.8Ki (+31.90% / 175.7Ki) | 3.484k (+0.06% / 3.482k) |
41+
| [Without comment characters](../testdata/small_no_comment_runes.json) | 113.1µ (+17.45% / 96.32µ) | 175.7Ki (+0.01% / 175.7Ki) | 3.482k (~% / 3.482k) |
42+
43+
### [`github.com/goccy/go-json`](https://github.com/goccy/go-json)
44+
45+
| **Small data set** | s/op | B/op | allocs/op |
46+
| -------------------------------------------------------------------------------------- | ------------------------- | ----------------------- | ----------------------- |
47+
| [With comments](../testdata/small.json) | 1.794µ | 1.047Ki | 10.00 |
48+
| [Without comments](../testdata/small_uncommented.json) (comment characters in strings) | 1.797µ (+15.38% / 1.557µ) | 928.0 (+20.83% / 768.0) | 10.00 (+11.11% / 9.000) |
49+
| [Without comment characters](../testdata/small_no_comment_runes.json) | 1.705µ (+3.30% / 1.651µ) | 768.0 (~% / 768.0) | 9.00 (~% / 9.000) |
50+
51+
| **Medium data set** | s/op | B/op | allocs/op |
52+
| -------------------------------------------------------------------------------------- | ------------------------- | --------------------------- | ---------------------- |
53+
| [With comments](../testdata/small.json) | 213.1µ | 434.9Ki | 77.00 |
54+
| [Without comments](../testdata/small_uncommented.json) (comment characters in strings) | 101.4µ (+83.61% / 55.24µ) | 250.4Ki (+28.94% / 194.2Ki) | 73.00 (+2.82% / 71.00) |
55+
| [Without comment characters](../testdata/small_no_comment_runes.json) | 72.60µ (+37.97% / 52.62µ) | 194.2Ki (+0.02% / 194.1Ki) | 71.00 (~% / 71.00) |

benchmarks/go_json.txt

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
goos: darwin
2+
goarch: arm64
3+
pkg: github.com/marcozac/go-jsonc
4+
BenchmarkUnmarshal/Small/Commented-10 686788 1689 ns/op 1072 B/op 10 allocs/op
5+
BenchmarkUnmarshal/Small/Commented-10 850492 1848 ns/op 1072 B/op 10 allocs/op
6+
BenchmarkUnmarshal/Small/Commented-10 672508 1681 ns/op 1072 B/op 10 allocs/op
7+
BenchmarkUnmarshal/Small/Commented-10 844490 1791 ns/op 1072 B/op 10 allocs/op
8+
BenchmarkUnmarshal/Small/Commented-10 808563 1739 ns/op 1072 B/op 10 allocs/op
9+
BenchmarkUnmarshal/Small/Commented-10 668275 1796 ns/op 1072 B/op 10 allocs/op
10+
BenchmarkUnmarshal/Small/Commented-10 863780 1762 ns/op 1072 B/op 10 allocs/op
11+
BenchmarkUnmarshal/Small/Commented-10 857653 1842 ns/op 1072 B/op 10 allocs/op
12+
BenchmarkUnmarshal/Small/Commented-10 593972 1863 ns/op 1072 B/op 10 allocs/op
13+
BenchmarkUnmarshal/Small/Commented-10 568066 1861 ns/op 1072 B/op 10 allocs/op
14+
BenchmarkUnmarshal/Small/UnCommented-10 829736 1791 ns/op 928 B/op 10 allocs/op
15+
BenchmarkUnmarshal/Small/UnCommented-10 822922 1788 ns/op 928 B/op 10 allocs/op
16+
BenchmarkUnmarshal/Small/UnCommented-10 728328 1802 ns/op 928 B/op 10 allocs/op
17+
BenchmarkUnmarshal/Small/UnCommented-10 741534 1761 ns/op 928 B/op 10 allocs/op
18+
BenchmarkUnmarshal/Small/UnCommented-10 855846 1854 ns/op 928 B/op 10 allocs/op
19+
BenchmarkUnmarshal/Small/UnCommented-10 740926 2052 ns/op 928 B/op 10 allocs/op
20+
BenchmarkUnmarshal/Small/UnCommented-10 737062 1806 ns/op 928 B/op 10 allocs/op
21+
BenchmarkUnmarshal/Small/UnCommented-10 753429 1750 ns/op 928 B/op 10 allocs/op
22+
BenchmarkUnmarshal/Small/UnCommented-10 679948 1812 ns/op 928 B/op 10 allocs/op
23+
BenchmarkUnmarshal/Small/UnCommented-10 666391 1790 ns/op 928 B/op 10 allocs/op
24+
BenchmarkUnmarshal/Small/NoCommentRunes-10 678354 1667 ns/op 768 B/op 9 allocs/op
25+
BenchmarkUnmarshal/Small/NoCommentRunes-10 828259 1699 ns/op 768 B/op 9 allocs/op
26+
BenchmarkUnmarshal/Small/NoCommentRunes-10 816710 1626 ns/op 768 B/op 9 allocs/op
27+
BenchmarkUnmarshal/Small/NoCommentRunes-10 873766 1685 ns/op 768 B/op 9 allocs/op
28+
BenchmarkUnmarshal/Small/NoCommentRunes-10 864908 1668 ns/op 768 B/op 9 allocs/op
29+
BenchmarkUnmarshal/Small/NoCommentRunes-10 819601 1711 ns/op 768 B/op 9 allocs/op
30+
BenchmarkUnmarshal/Small/NoCommentRunes-10 856377 1827 ns/op 768 B/op 9 allocs/op
31+
BenchmarkUnmarshal/Small/NoCommentRunes-10 730975 1859 ns/op 768 B/op 9 allocs/op
32+
BenchmarkUnmarshal/Small/NoCommentRunes-10 595267 1766 ns/op 768 B/op 9 allocs/op
33+
BenchmarkUnmarshal/Small/NoCommentRunes-10 588590 1787 ns/op 768 B/op 9 allocs/op
34+
BenchmarkUnmarshal/Medium/Commented-10 5258 206950 ns/op 445105 B/op 75 allocs/op
35+
BenchmarkUnmarshal/Medium/Commented-10 5463 216683 ns/op 445111 B/op 76 allocs/op
36+
BenchmarkUnmarshal/Medium/Commented-10 5232 211923 ns/op 445327 B/op 77 allocs/op
37+
BenchmarkUnmarshal/Medium/Commented-10 5164 216470 ns/op 445287 B/op 77 allocs/op
38+
BenchmarkUnmarshal/Medium/Commented-10 5502 212132 ns/op 445343 B/op 77 allocs/op
39+
BenchmarkUnmarshal/Medium/Commented-10 5478 214793 ns/op 445253 B/op 76 allocs/op
40+
BenchmarkUnmarshal/Medium/Commented-10 5323 211259 ns/op 445318 B/op 77 allocs/op
41+
BenchmarkUnmarshal/Medium/Commented-10 5906 212906 ns/op 445451 B/op 78 allocs/op
42+
BenchmarkUnmarshal/Medium/Commented-10 5524 213206 ns/op 445385 B/op 77 allocs/op
43+
BenchmarkUnmarshal/Medium/Commented-10 5792 231255 ns/op 445199 B/op 76 allocs/op
44+
BenchmarkUnmarshal/Medium/UnCommented-10 12003 100052 ns/op 256381 B/op 73 allocs/op
45+
BenchmarkUnmarshal/Medium/UnCommented-10 12105 102861 ns/op 256371 B/op 73 allocs/op
46+
BenchmarkUnmarshal/Medium/UnCommented-10 12154 97993 ns/op 256396 B/op 73 allocs/op
47+
BenchmarkUnmarshal/Medium/UnCommented-10 10000 106910 ns/op 256359 B/op 73 allocs/op
48+
BenchmarkUnmarshal/Medium/UnCommented-10 10000 116319 ns/op 256268 B/op 73 allocs/op
49+
BenchmarkUnmarshal/Medium/UnCommented-10 10000 100183 ns/op 256400 B/op 73 allocs/op
50+
BenchmarkUnmarshal/Medium/UnCommented-10 12264 101129 ns/op 256388 B/op 73 allocs/op
51+
BenchmarkUnmarshal/Medium/UnCommented-10 10000 107515 ns/op 256349 B/op 73 allocs/op
52+
BenchmarkUnmarshal/Medium/UnCommented-10 10000 100481 ns/op 256337 B/op 73 allocs/op
53+
BenchmarkUnmarshal/Medium/UnCommented-10 12072 101738 ns/op 256374 B/op 73 allocs/op
54+
BenchmarkUnmarshal/Medium/NoCommentRunes-10 13926 74253 ns/op 198819 B/op 71 allocs/op
55+
BenchmarkUnmarshal/Medium/NoCommentRunes-10 16582 72215 ns/op 198853 B/op 71 allocs/op
56+
BenchmarkUnmarshal/Medium/NoCommentRunes-10 16869 71500 ns/op 198832 B/op 71 allocs/op
57+
BenchmarkUnmarshal/Medium/NoCommentRunes-10 16485 76287 ns/op 198841 B/op 71 allocs/op
58+
BenchmarkUnmarshal/Medium/NoCommentRunes-10 16418 71112 ns/op 198847 B/op 71 allocs/op
59+
BenchmarkUnmarshal/Medium/NoCommentRunes-10 16806 71753 ns/op 198842 B/op 71 allocs/op
60+
BenchmarkUnmarshal/Medium/NoCommentRunes-10 16928 72978 ns/op 198842 B/op 71 allocs/op
61+
BenchmarkUnmarshal/Medium/NoCommentRunes-10 16573 75274 ns/op 198820 B/op 71 allocs/op
62+
BenchmarkUnmarshal/Medium/NoCommentRunes-10 16704 71288 ns/op 198851 B/op 71 allocs/op
63+
BenchmarkUnmarshal/Medium/NoCommentRunes-10 16976 73778 ns/op 198831 B/op 71 allocs/op
64+
PASS
65+
ok github.com/marcozac/go-jsonc 91.252s

0 commit comments

Comments
 (0)