Replace jsonparser to fastjson for parsing json

This commit is contained in:
Marius Orcsik 2021-08-14 18:34:02 +02:00
parent ca0c565b6f
commit 73041d04eb
No known key found for this signature in database
GPG key ID: DBF5E47F5DBC4D21
8 changed files with 182 additions and 180 deletions

View file

@ -7,7 +7,7 @@ import (
"time"
"unsafe"
"github.com/buger/jsonparser"
"github.com/valyala/fastjson"
)
// CanReceiveActivities Types
@ -195,20 +195,14 @@ type PublicKey struct {
}
func (p *PublicKey) UnmarshalJSON(data []byte) error {
if id, err := jsonparser.GetString(data, "id"); err == nil {
if id := fastjson.GetString(data, "id"); len(id) > 0 {
p.ID = ID(id)
} else {
return err
}
if o, err := jsonparser.GetString(data, "owner"); err == nil {
if o := fastjson.GetString(data, "owner"); len(o) > 0 {
p.Owner = IRI(o)
} else {
return err
}
if pub, err := jsonparser.GetString(data, "publicKeyPem"); err == nil {
if pub := fastjson.GetString(data, "publicKeyPem"); len(pub) > 0 {
p.PublicKeyPem = pub
} else {
return err
}
return nil
}

View file

@ -7,10 +7,9 @@ import (
"fmt"
"net/url"
"reflect"
"strconv"
"time"
"github.com/buger/jsonparser"
"github.com/valyala/fastjson"
)
var (
@ -28,26 +27,17 @@ var ItemTyperFunc TyperFn = GetItemByType
type TyperFn func(ActivityVocabularyType) (Item, error)
func JSONGetID(data []byte) ID {
i, err := jsonparser.GetString(data, "id")
if err != nil {
return ""
}
i := fastjson.GetString(data, "id")
return ID(i)
}
func JSONGetType(data []byte) ActivityVocabularyType {
t, err := jsonparser.GetString(data, "type")
if err != nil {
return ""
}
t := fastjson.GetBytes(data, "type")
return ActivityVocabularyType(t)
}
func JSONGetMimeType(data []byte, prop string) MimeType {
t, err := jsonparser.GetString(data, prop)
if err != nil {
return ""
}
t := fastjson.GetString(data, prop)
return MimeType(t)
}
@ -55,73 +45,59 @@ func JSONGetInt(data []byte, prop string) int64 {
if len(data) == 0 {
return 0
}
val, err := jsonparser.GetInt(data, prop)
if err != nil {
// try to load integers encapsulated in quotes
if sn, err := jsonparser.GetString(data, prop); err == nil {
val, _ = strconv.ParseInt(sn, 10, 64)
}
}
return val
val := fastjson.GetInt(data, prop)
return int64(val)
}
func JSONGetFloat(data []byte, prop string) float64 {
if len(data) == 0 {
return 0
}
val, err := jsonparser.GetFloat(data, prop)
if err != nil {
// try to load floats encapsulated in quotes
if sn, err := jsonparser.GetString(data, prop); err == nil {
val, _ = strconv.ParseFloat(sn, 64)
}
}
val := fastjson.GetFloat64(data, prop)
return val
}
func JSONGetString(data []byte, prop string) string {
val, err := jsonparser.GetString(data, prop)
if err != nil {
}
val := fastjson.GetString(data, prop)
return val
}
func JSONGetBytes(data []byte, prop string) []byte {
val, _, _, err := jsonparser.Get(data, prop)
if err != nil {
}
val := fastjson.GetBytes(data, prop)
return val
}
func JSONGetBoolean(data []byte, prop string) bool {
val, err := jsonparser.GetBoolean(data, prop)
if err != nil {
}
val := fastjson.GetBool(data, prop)
return val
}
func JSONGetNaturalLanguageField(data []byte, prop string) NaturalLanguageValues {
n := NaturalLanguageValues{}
val, typ, _, err := jsonparser.Get(data, prop)
if err != nil || val == nil {
p := fastjson.Parser{}
val, err := p.ParseBytes(data)
if err != nil {
return nil
}
switch typ {
case jsonparser.Object:
jsonparser.ObjectEach(val, func(key []byte, value []byte, dataType jsonparser.ValueType, offset int) error {
if dataType == jsonparser.String {
l := LangRefValue{}
if err := l.UnmarshalJSON(value); err == nil {
if l.Ref != NilLangRef || len(l.Value) > 0 {
n = append(n, l)
}
v := val.Get(prop)
if v == nil {
return nil
}
switch v.Type() {
case fastjson.TypeObject:
ob, _ := v.Object()
ob.Visit(func(key []byte, v *fastjson.Value) {
l := LangRefValue{}
l.Ref = LangRef(key)
if err := l.Value.UnmarshalJSON(v.GetStringBytes()); err == nil {
if l.Ref != NilLangRef || len(l.Value) > 0 {
n = append(n, l)
}
}
return err
})
case jsonparser.String:
case fastjson.TypeString:
l := LangRefValue{}
if err := l.UnmarshalJSON(val); err == nil {
if err := l.UnmarshalJSON(v.GetStringBytes()); err == nil {
n = append(n, l)
}
}
@ -131,15 +107,15 @@ func JSONGetNaturalLanguageField(data []byte, prop string) NaturalLanguageValues
func JSONGetTime(data []byte, prop string) time.Time {
t := time.Time{}
if str, _ := jsonparser.GetUnsafeString(data, prop); len(str) > 0 {
t.UnmarshalText([]byte(str))
if str := fastjson.GetBytes(data, prop); len(str) > 0 {
t.UnmarshalText(str)
return t.UTC()
}
return t
}
func JSONGetDuration(data []byte, prop string) time.Duration {
if str, _ := jsonparser.GetUnsafeString(data, prop); len(str) > 0 {
if str := fastjson.GetString(data, prop); len(str) > 0 {
// TODO(marius): this needs to be replaced to be compatible with xsd:duration
d, _ := time.ParseDuration(str)
return d
@ -195,31 +171,32 @@ func JSONUnmarshalToItem(data []byte) Item {
if ItemTyperFunc == nil {
return nil
}
val, typ, _, err := jsonparser.Get(data)
if err != nil || len(val) == 0 {
p := fastjson.Parser{}
val, err := p.ParseBytes(data)
if err != nil {
return nil
}
var i Item
switch typ {
case jsonparser.Array:
switch val.Type() {
case fastjson.TypeArray:
items := make(ItemCollection, 0)
jsonparser.ArrayEach(data, func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
for _, v := range val.GetArray() {
var it Item
it, err = itemFn(value)
it, err = itemFn(v.GetStringBytes())
if it != nil && err == nil {
items.Append(it)
}
})
}
if len(items) == 1 {
i = items.First()
}
if len(items) > 1 {
i = items
}
case jsonparser.Object:
case fastjson.TypeObject:
i, err = itemFn(data)
case jsonparser.String:
case fastjson.TypeString:
if iri, ok := asIRI(data); ok {
// try to see if it's an IRI
i = iri
@ -245,27 +222,28 @@ func asIRI(val []byte) (IRI, bool) {
}
func JSONGetItem(data []byte, prop string) Item {
val, typ, _, err := jsonparser.Get(data, prop)
p := fastjson.Parser{}
val, err := p.ParseBytes(data)
if err != nil {
return nil
}
switch typ {
case jsonparser.String:
if i, ok := asIRI(val); ok {
val = val.Get(prop)
if val == nil {
return nil
}
switch val.Type() {
case fastjson.TypeString:
if i, ok := asIRI(val.GetStringBytes()); ok {
// try to see if it's an IRI
return i
}
case jsonparser.Array:
case fastjson.TypeArray:
return JSONGetItems(data, prop)
case jsonparser.Object:
return JSONUnmarshalToItem(val)
case jsonparser.Number:
case fastjson.TypeObject:
return JSONUnmarshalToItem(val.GetStringBytes())
case fastjson.TypeNumber:
fallthrough
case jsonparser.Boolean:
fallthrough
case jsonparser.Null:
fallthrough
case jsonparser.Unknown:
case fastjson.TypeNull:
fallthrough
default:
return nil
@ -274,34 +252,40 @@ func JSONGetItem(data []byte, prop string) Item {
}
func JSONGetURIItem(data []byte, prop string) Item {
val, typ, _, err := jsonparser.Get(data, prop)
p := fastjson.Parser{}
val, err := p.ParseBytes(data)
if err != nil {
return nil
}
v := val.Get(prop)
if v == nil {
return nil
}
switch typ {
case jsonparser.Object:
switch v.Type() {
case fastjson.TypeObject:
return JSONGetItem(data, prop)
case jsonparser.Array:
case fastjson.TypeArray:
it := make(ItemCollection, 0)
jsonparser.ArrayEach(val, func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
for _, ob := range v.GetArray() {
value := ob.GetStringBytes()
if i, ok := asIRI(value); ok {
it.Append(i)
return
continue
}
i, err := ItemTyperFunc(JSONGetType(value))
if err != nil {
return
continue
}
err = i.(json.Unmarshaler).UnmarshalJSON(value)
if err != nil {
return
if err = i.(json.Unmarshaler).UnmarshalJSON(value); err != nil {
continue
}
it.Append(i)
})
}
return it
case jsonparser.String:
return IRI(val)
case fastjson.TypeString:
return IRI(val.String())
}
return nil
@ -311,25 +295,32 @@ func JSONGetItems(data []byte, prop string) ItemCollection {
if len(data) == 0 {
return nil
}
val, typ, _, err := jsonparser.Get(data, prop)
if err != nil || len(val) == 0 {
p := fastjson.Parser{}
v, err := p.ParseBytes(data)
if err != nil {
return nil
}
it := make(ItemCollection, 0)
switch typ {
case jsonparser.Array:
jsonparser.ArrayEach(data, func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
i, err := itemFn(value)
if i != nil && err == nil {
switch v.Type() {
case fastjson.TypeArray:
val := v.GetArray(prop)
if len(val) == 0 {
return nil
}
for _, v := range val {
if i, err := itemFn(v.GetStringBytes()); i != nil && err == nil {
it.Append(i)
}
}, prop)
case jsonparser.Object:
it.Append(JSONGetItem(data, prop))
case jsonparser.String:
if len(val) > 0 {
it.Append(IRI(val))
}
case fastjson.TypeObject:
if i := JSONGetItem(data, prop); i != nil {
it.Append(i)
}
case fastjson.TypeString:
if iri := v.GetStringBytes(); len(iri) > 0 {
it.Append(IRI(iri))
}
}
if len(it) == 0 {
@ -339,18 +330,12 @@ func JSONGetItems(data []byte, prop string) ItemCollection {
}
func JSONGetLangRefField(data []byte, prop string) LangRef {
val, err := jsonparser.GetString(data, prop)
if err != nil {
return ""
}
val := fastjson.GetString(data, prop)
return LangRef(val)
}
func JSONGetIRI(data []byte, prop string) IRI {
val, err := jsonparser.GetString(data, prop)
if err != nil {
return ""
}
val := fastjson.GetString(data, prop)
return IRI(val)
}
@ -483,12 +468,12 @@ func GetItemByType(typ ActivityVocabularyType) (Item, error) {
}
func JSONGetActorEndpoints(data []byte, prop string) *Endpoints {
str, _ := jsonparser.GetUnsafeString(data, prop)
str := fastjson.GetBytes(data, prop)
var e *Endpoints
if len(str) > 0 {
e = &Endpoints{}
e.UnmarshalJSON([]byte(str))
e.UnmarshalJSON(str)
}
return e

View file

@ -313,40 +313,49 @@ func TestJSONGetItemByType(t *testing.T) {
}
func TestUnmarshalJSON(t *testing.T) {
type args struct {
d []byte
}
tests := []struct {
name string
args args
want Item
name string
data []byte
want Item
err error
}{
{
name: "empty",
args: args{[]byte{'{', '}'}},
want: nil,
err: nil,
name: "empty",
data: []byte{'{', '}'},
want: nil,
err: nil,
},
{
name: "IRI",
args: args{[]byte("\"http://example.com\"")},
want: IRI("http://example.com"),
err: nil,
name: "IRI",
data: []byte(`"http://example.com"`),
want: IRI("http://example.com"),
err: nil,
},
{
name: "IRIs",
args: args{[]byte(fmt.Sprintf("[%q, %q]", "http://example.com", "http://example.net"))},
name: "IRIs",
data: []byte(fmt.Sprintf("[%q, %q]", "http://example.com", "http://example.net")),
want: ItemCollection{
IRI("http://example.com"),
IRI("http://example.net"),
},
err: nil,
},
{
name: "object",
data: []byte(`{"type":"Note"}`),
want: &Object{Type: NoteType},
err: nil,
},
{
name: "activity",
data: []byte(`{"type":"Like"}`),
want: &Activity{Type: LikeType},
err: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := UnmarshalJSON(tt.args.d)
got, err := UnmarshalJSON(tt.data)
if (err != nil && tt.err == nil) || (err == nil && tt.err != nil) {
if !errors.Is(err, tt.err) {
t.Errorf("UnmarshalJSON() error = %v, wantErr %v", err, tt.err)

View file

@ -3,9 +3,10 @@ package activitypub
import (
"encoding/json"
"fmt"
"time"
"git.sr.ht/~mariusor/go-xsd-duration"
"github.com/go-ap/jsonld"
"time"
)
func writeComma(b *[]byte) {

2
go.mod
View file

@ -4,6 +4,6 @@ go 1.13
require (
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20200411073322-f0bcc40f0bf2
github.com/buger/jsonparser v1.0.0
github.com/go-ap/jsonld v0.0.0-20200327122108-fafac2de2660
github.com/valyala/fastjson v1.6.3
)

20
iri.go
View file

@ -2,10 +2,11 @@ package activitypub
import (
"fmt"
"github.com/buger/jsonparser"
"net/url"
"path"
"strings"
"github.com/valyala/fastjson"
)
const (
@ -154,21 +155,22 @@ func (i *IRIs) UnmarshalJSON(data []byte) error {
if i == nil {
return nil
}
value, typ, _, err := jsonparser.Get(data)
p := fastjson.Parser{}
value, err := p.ParseBytes(data)
if err != nil {
return err
}
switch typ {
case jsonparser.String:
if iri, ok := asIRI(value); ok {
switch value.Type() {
case fastjson.TypeString:
if iri, ok := asIRI(value.GetStringBytes()); ok && len(iri) > 0 {
*i = append(*i, iri)
}
case jsonparser.Array:
jsonparser.ArrayEach(data, func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
if iri, ok := asIRI(value); ok {
case fastjson.TypeArray:
for _, v := range value.GetArray() {
if iri, ok := asIRI(v.GetStringBytes()); ok && len(iri) > 0 {
*i = append(*i, iri)
}
})
}
}
return nil
}

View file

@ -6,7 +6,7 @@ import (
"strconv"
"strings"
"github.com/buger/jsonparser"
"github.com/valyala/fastjson"
)
const NilLangRef LangRef = "-"
@ -164,22 +164,23 @@ func (l LangRefValue) String() string {
// UnmarshalJSON implements the JsonEncoder interface
func (l *LangRefValue) UnmarshalJSON(data []byte) error {
_, typ, _, err := jsonparser.Get(data)
p := fastjson.Parser{}
val, err := p.ParseBytes(data)
if err != nil {
l.Ref = NilLangRef
l.Value = Content(unescape(data))
l.Value = unescape(data)
return nil
}
switch typ {
case jsonparser.Object:
jsonparser.ObjectEach(data, func(key []byte, value []byte, dataType jsonparser.ValueType, offset int) error {
switch val.Type() {
case fastjson.TypeObject:
o, _ := val.Object()
o.Visit(func(key []byte, v *fastjson.Value) {
l.Ref = LangRef(key)
l.Value = Content(unescape(value))
return err
l.Value = unescape(v.GetStringBytes())
})
case jsonparser.String:
case fastjson.TypeString:
l.Ref = NilLangRef
l.Value = Content(unescape(data))
l.Value = unescape(val.GetStringBytes())
}
return nil
@ -187,6 +188,8 @@ func (l *LangRefValue) UnmarshalJSON(data []byte) error {
// UnmarshalText implements the TextEncoder interface
func (l *LangRefValue) UnmarshalText(data []byte) error {
l.Ref = NilLangRef
l.Value = unescape(data)
return nil
}
@ -277,7 +280,7 @@ func (c Content) Equals(other Content) bool {
}
func unescape(b []byte) []byte {
// FIMXE(marius): I feel like I'm missing something really obvious about encoding/decoding from Json regarding
// FIXME(marius): I feel like I'm missing something really obvious about encoding/decoding from Json regarding
// escape characters, and that this function is just a hack. Be better future Marius, find the real problem!
b = bytes.ReplaceAll(b, []byte{'\\', 'a'}, []byte{'\a'})
b = bytes.ReplaceAll(b, []byte{'\\', 'f'}, []byte{'\f'})
@ -292,26 +295,33 @@ func unescape(b []byte) []byte {
// UnmarshalJSON tries to load the NaturalLanguage array from the incoming json value
func (n *NaturalLanguageValues) UnmarshalJSON(data []byte) error {
val, typ, _, err := jsonparser.Get(data)
p := fastjson.Parser{}
val, err := p.ParseBytes(data)
if err != nil {
// try our luck if data contains an unquoted string
n.Append(NilLangRef, Content(unescape(data)))
n.Append(NilLangRef, unescape(data))
return nil
}
switch typ {
case jsonparser.Object:
jsonparser.ObjectEach(data, func(key []byte, val []byte, dataType jsonparser.ValueType, offset int) error {
n.Append(LangRef(key), Content(unescape(val)))
return err
switch val.Type() {
case fastjson.TypeObject:
ob, _ := val.Object()
ob.Visit(func(key []byte, v *fastjson.Value) {
if dat := v.GetStringBytes(); len(dat) > 0 {
n.Append(LangRef(key), unescape(dat))
}
})
case jsonparser.String:
n.Append(NilLangRef, Content(unescape(val)))
case jsonparser.Array:
jsonparser.ArrayEach(data, func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
case fastjson.TypeString:
if dat := val.GetStringBytes(); len(dat) > 0 {
n.Append(NilLangRef, unescape(dat))
}
case fastjson.TypeArray:
for _, v := range val.GetArray() {
l := LangRefValue{}
l.UnmarshalJSON(value)
n.Append(l.Ref, l.Value)
})
l.UnmarshalJSON([]byte(v.String()))
if len(l.Value) > 0 {
n.Append(l.Ref, l.Value)
}
}
}
return nil

View file

@ -2,11 +2,12 @@ package activitypub
import (
"fmt"
"github.com/buger/jsonparser"
"reflect"
"strings"
"time"
"unsafe"
"github.com/valyala/fastjson"
)
const (
@ -480,10 +481,10 @@ type Source struct {
func GetAPSource(data []byte) Source {
s := Source{}
if contBytes, _, _, err := jsonparser.Get(data, "source", "content"); err == nil {
if contBytes := fastjson.GetBytes(data, "source", "content"); len(contBytes) > 0 {
s.Content.UnmarshalJSON(contBytes)
}
if mimeBytes, _, _, err := jsonparser.Get(data, "source", "mediaType"); err == nil {
if mimeBytes := fastjson.GetBytes(data, "source", "mediaType"); len(mimeBytes) > 0 {
s.MediaType.UnmarshalJSON(mimeBytes)
}