Skip to content

Commit

Permalink
Merge pull request #28 from petemcmahon365/master
Browse files Browse the repository at this point in the history
escape newline, carriage return, tab with "\n","\r","\t" Thanks @petemcmahon365
  • Loading branch information
kungfusheep committed Jun 5, 2023
2 parents 2e09e22 + 262763c commit 1ae2e35
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 5 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,9 @@ There are a couple of subtle ways you can configure the encoders.
* It supports the same `json:"tag,options"` syntax as the stdlib, but not the same options. Currently the options you have are
- `,stringer`, which instead of the standard serialization method for a given type, nominates that its `.String()` function is invoked instead to provide the serialization value.
- `,raw`, which allows byteslice-like items (like `[]byte` and `string`) to be written to the buffer directly with no conversion, quoting or otherwise. `nil` or empty fields annotated as `raw` will output `null`.
- `,encoder` which instead of the standard serialization method for a given type, nominates that its `.JSONEncode(*jingo.Buffer)` function is invoked instead. From there you can manually write to the buffer for that particular field. The interface you need to comply with is exported as `jingo.JSONEncoder`.
- `,escape`, which safely escapes `"` and `\` characters to valid JSON whilst writing. To get the same functionality when using `SliceEncoder` on its own, use `jingo.EscapeString` to initialize the encoder - e.g `NewSliceEncoder([]jingo.EscapeString)` - instead of `string` directly. There is obviously a performance impact on the write speed using this option, the benchmarks show it takes twice the time of a standard string write, so whilst it is still faster than using the stdlib, to get the best performance it is recommended to only be used when needed and only then when the escaping work can't be done up-front.
- `,encoder` which instead of the standard serialization method for a given type, nominates that its `.JSONEncode(*jingo.Buffer)` function is invoked instead. From there you can manually write to the buffer for that particular field. The interface you need to comply with is exported as `jingo.JSONEncoder`.
- `,escape`, which safely escapes `"`,`\`, line feed (`\n`), carriage return (`\r`) and tab (`\t`) characters to valid JSON whilst writing. To get the same functionality when using `SliceEncoder` on its own, use `jingo.EscapeString` to initialize the encoder - e.g `NewSliceEncoder([]jingo.EscapeString)` - instead of `string` directly. There is obviously a performance impact on the write speed using this option, the benchmarks show it takes twice the time of a standard string write, so whilst it is still faster than using the stdlib, to get the best performance it is recommended to only be used when needed and only then when the escaping work can't be done up-front.


## How does it work

Expand Down
17 changes: 14 additions & 3 deletions jingo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"encoding/json"
"fmt"
"reflect"
"strconv"
"testing"
"time"
Expand Down Expand Up @@ -148,11 +149,14 @@ type StructWithEscapes struct {

func Test_StructWithEscapes(t *testing.T) {
es := StructWithEscapes{
String: "one\\two\\,three\"",
StringArray: []string{"one\\two", "three\\,four"},
String: `one\two\,three,
four"`, //N.B. includes 2 indentation tabs
StringArray: []string{`one\two`, `three\,four,
five`, //N.B. includes 2 indentation tabs
},
}

wantJSON := `{"str":"one\\two\\,three\"","str-array":["one\\two","three\\,four"]}`
wantJSON := `{"str":"one\\two\\,three,\n\t\tfour\"","str-array":["one\\two","three\\,four,\n\t\tfive"]}`

var enc = NewStructEncoder(StructWithEscapes{})
buf := NewBufferFromPool()
Expand All @@ -168,6 +172,13 @@ func Test_StructWithEscapes(t *testing.T) {
if resultJSON != wantJSON {
t.Errorf("Test_StructWithScapes Failed: want JSON:" + wantJSON + " got JSON:" + resultJSON)
}

andBackAgain := StructWithEscapes{}
json.Unmarshal([]byte(resultJSON), &andBackAgain)

if !reflect.DeepEqual(es, andBackAgain) {
t.Errorf("Test_StructWithScapes Failed: want: %+v got: %+v", es, andBackAgain)
}
}

type UnicodeObject struct {
Expand Down
21 changes: 21 additions & 0 deletions ptrconvert.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,27 @@ func ptrEscapeStringToBuf(v unsafe.Pointer, w *Buffer) {

w.WriteByte('\\')
w.WriteByte(bs[i])
case '\n':
if pos < i {
w.WriteString(bs[pos:i])
}
pos = i + 1

w.WriteString(`\n`)
case '\r':
if pos < i {
w.WriteString(bs[pos:i])
}
pos = i + 1

w.WriteString(`\r`)
case '\t':
if pos < i {
w.WriteString(bs[pos:i])
}
pos = i + 1

w.WriteString(`\t`)
}
}

Expand Down

0 comments on commit 1ae2e35

Please sign in to comment.