Improvements to Object, NaturalLanguage and Source Json marshaling

Improvements to tests
This commit is contained in:
Marius Orcsik 2019-12-18 10:02:23 +01:00
parent 88cce1d2f9
commit 8420879a85
No known key found for this signature in database
GPG key ID: 889CE8E4FB2D877A
4 changed files with 407 additions and 88 deletions

View file

@ -4,7 +4,6 @@ import (
"bytes"
"fmt"
"github.com/buger/jsonparser"
json "github.com/go-ap/jsonld"
"strconv"
"strings"
)
@ -73,21 +72,47 @@ func (n *NaturalLanguageValues) Set(ref LangRef, v string) error {
// MarshalJSON serializes the NaturalLanguageValues into JSON
func (n NaturalLanguageValues) MarshalJSON() ([]byte, error) {
if len(n) == 0 {
return json.Marshal(nil)
l := len(n)
if l == 0 {
return nil, nil
}
if len(n) == 1 {
b := bytes.Buffer{}
if l == 1 {
v := n[0]
if v.Ref == NilLangRef {
return v.MarshalJSON()
if len(v.Value) > 0 {
v.Value = string(unescape([]byte(v.Value)))
ll, err := b.WriteString(strconv.Quote(v.Value))
if err != nil {
return nil, err
}
if ll == 0 {
return nil, nil
}
return b.Bytes(), nil
}
}
mm := make(map[LangRef]string)
b.Write([]byte{'{'})
empty := true
for _, val := range n {
mm[val.Ref] = val.Value
if len(val.Ref) == 0 || len(val.Value) == 0 {
continue
}
if !empty {
b.Write([]byte{','})
}
if v, err := val.MarshalJSON(); err == nil && len(v) > 0 {
l, err := b.Write(v)
if err == nil && l > 0 {
empty = false
}
}
}
return json.Marshal(mm)
b.Write([]byte{'}'})
if !empty {
return b.Bytes(), nil
}
return nil, nil
}
// First returns the first element in the array
@ -165,7 +190,7 @@ func (l *LangRefValue) UnmarshalText(data []byte) error {
// MarshalJSON serializes the LangRefValue into JSON
func (l LangRefValue) MarshalJSON() ([]byte, error) {
buf := bytes.Buffer{}
if l.Ref != NilLangRef {
if l.Ref != NilLangRef && len(l.Ref) > 0{
if l.Value == "" {
return nil, nil
}

View file

@ -36,7 +36,7 @@ func TestNaturalLanguageValue_MarshalJSON(t *testing.T) {
if err1 != nil {
t.Errorf("Error: '%s'", err1)
}
txt := `{"en":"the test"}`
txt := `"the test"`
if txt != string(out1) {
t.Errorf("Different marshal result '%s', instead of '%s'", out1, txt)
}
@ -291,7 +291,7 @@ func TestNaturalLanguageValues_MarshalJSON(t *testing.T) {
if err != nil {
t.Errorf("Failed marshaling '%v'", err)
}
mRes := "{\"de\":\"test\",\"en\":\"test\"}"
mRes := "{\"en\":\"test\",\"de\":\"test\"}"
if string(result) != mRes {
t.Errorf("Different results '%v' vs. '%v'", string(result), mRes)
}
@ -307,7 +307,7 @@ func TestNaturalLanguageValues_MarshalJSON(t *testing.T) {
if err1 != nil {
t.Errorf("Failed marshaling '%v'", err1)
}
mRes1 := `{"en":"test"}`
mRes1 := `"test"`
if string(result1) != mRes1 {
t.Errorf("Different results '%v' vs. '%v'", string(result1), mRes1)
}
@ -343,7 +343,7 @@ func TestNaturalLanguageValues_MarshalJSON(t *testing.T) {
if j == nil {
t.Errorf("Error marshaling: nil value returned")
}
expected := fmt.Sprintf("{\"%s\":\"%s\"}", nlv.Ref, nlv.Value)
expected := fmt.Sprintf("\"%s\"", nlv.Value)
if string(j) != expected {
t.Errorf("Wrong value: %s, expected %s", j, expected)
}

168
object.go
View file

@ -320,35 +320,57 @@ func (o *Object) UnmarshalJSON(data []byte) error {
return nil
}
func writeProp(b *bytes.Buffer, name string, val []byte) {
writePropName(b, name)
writeValue(b, val)
}
func writePropName(b *bytes.Buffer, s string) {
b.Write([]byte{'"'})
b.WriteString(s)
b.Write([]byte{'"', ':'})
}
func writeValue(b *bytes.Buffer, s []byte) {
b.Write([]byte{'"'})
b.Write(s)
b.Write([]byte{'"'})
}
func writeNaturalLanguageProp (b *bytes.Buffer, n string, nl NaturalLanguageValues) {
if v, err := nl.MarshalJSON(); err == nil {
if nl.Count() > 1 {
n += "Map"
}
writeProp(b, n, v)
func writeProp(b *bytes.Buffer, name string, val []byte) (notEmpty bool) {
notEmpty = false
if len(val) == 0 {
return notEmpty
}
writePropName(b, name)
return writeValue(b, val)
}
func writeItemProp (b *bytes.Buffer, n string, i Item) {
func writePropName(b *bytes.Buffer, s string) (notEmpty bool) {
if len(s) == 0 {
return false
}
b.Write([]byte{'"'})
l, err := b.WriteString(s)
b.Write([]byte{'"', ':'})
if err != nil {
return false
}
if l == 0 {
return false
}
return true
}
func writeValue(b *bytes.Buffer, s []byte) (notEmpty bool) {
l, err := b.Write(s)
if err != nil {
return false
}
if l == 0 {
return false
}
return true
}
func writeNaturalLanguageProp(b *bytes.Buffer, n string, nl NaturalLanguageValues) (notEmpty bool) {
l := nl.Count()
if l > 1 {
n += "Map"
}
if v, err := nl.MarshalJSON(); err == nil && len(v) > 0 {
notEmpty = writeProp(b, n, v)
}
return notEmpty
}
func writeItemProp(b *bytes.Buffer, n string, i Item) (notEmpty bool) {
notEmpty = false
if i == nil {
b.WriteString("nil")
return
return notEmpty
}
if i.IsObject() {
OnObject(i, func(o *Object) error {
@ -356,54 +378,68 @@ func writeItemProp (b *bytes.Buffer, n string, i Item) {
if err != nil {
return nil
}
writeProp(b, n, v)
notEmpty = writeProp(b, n, v)
return nil
})
} else if i.IsCollection() {
OnCollection(i, func(c CollectionInterface) error {
writeItemCollectionProp(b, n, c.Collection())
notEmpty = writeItemCollectionProp(b, n, c.Collection())
return nil
})
}
return notEmpty
}
func writeItemCollectionProp (b *bytes.Buffer, n string, i ItemCollection) {
func writeItemCollectionProp(b *bytes.Buffer, n string, i ItemCollection) (notEmpty bool) {
return false
}
// MarshalJSON
func (o Object) MarshalJSON() ([]byte, error) {
b := bytes.Buffer{}
writeComma := func () { b.WriteString(",") }
if v, err := o.ID.MarshalJSON(); err == nil {
writeProp(&b, "id", v)
writeComma()
}
if v, err := o.Type.MarshalJSON(); err == nil {
writeProp(&b, "type", v)
writeComma()
}
if v, err := o.MediaType.MarshalJSON(); err == nil {
writeProp(&b, "mediaType", v)
writeComma()
}
if len(o.Name) > 0 {
writeNaturalLanguageProp(&b, "name", o.Name)
writeComma()
}
if len(o.Summary) > 0 {
writeNaturalLanguageProp(&b, "summary", o.Summary)
writeComma()
}
if len(o.Content) > 0 {
writeNaturalLanguageProp(&b, "content", o.Content)
writeComma()
notEmpty := false
b.Write([]byte{'{'})
writeComma := func() { b.WriteString(",") }
writeCommaIfNotEmpty := func(notEmpty bool) {
if notEmpty {
writeComma()
}
}
if v, err := o.Source.MarshalJSON(); err == nil {
writeProp(&b, "source", v)
if v, err := o.ID.MarshalJSON(); err == nil && len(v) > 0 {
notEmpty = writeProp(&b, "id", v)
}
return b.Bytes(), nil
if v, err := o.Type.MarshalJSON(); err == nil && len(v) > 0 {
writeCommaIfNotEmpty(notEmpty)
notEmpty = writeProp(&b, "type", v)
}
if v, err := o.MediaType.MarshalJSON(); err == nil && len(v) > 0 {
writeCommaIfNotEmpty(notEmpty)
notEmpty = writeProp(&b, "mediaType", v)
}
if len(o.Name) > 0 {
writeCommaIfNotEmpty(notEmpty)
notEmpty = writeNaturalLanguageProp(&b, "name", o.Name)
}
if len(o.Summary) > 0 {
writeCommaIfNotEmpty(notEmpty)
notEmpty = writeNaturalLanguageProp(&b, "summary", o.Summary)
}
if len(o.Content) > 0 {
writeCommaIfNotEmpty(notEmpty)
notEmpty = writeNaturalLanguageProp(&b, "content", o.Content)
}
if v, err := o.Source.MarshalJSON(); err == nil && len(v) > 0 {
writeCommaIfNotEmpty(notEmpty)
notEmpty = writeProp(&b, "source", v)
}
if notEmpty {
b.Write([]byte{'}'})
return b.Bytes(), nil
}
return nil, nil
}
// Recipients performs recipient de-duplication on the Object's To, Bto, CC and BCC properties
@ -578,20 +614,22 @@ func (s *Source) UnmarshalJSON(data []byte) error {
// MarshalJSON
func (s Source) MarshalJSON() ([]byte, error) {
b := bytes.Buffer{}
empty := true
b.Write([]byte{'{'})
if len(s.MediaType) > 0 {
if v, err := s.MediaType.MarshalJSON(); err == nil {
writePropName(&b, "mediaType")
b.Write(v)
b.Write([]byte{','})
if v, err := s.MediaType.MarshalJSON(); err == nil && len(v) > 0 {
empty = !writeProp(&b, "mediaType", v)
}
}
if len(s.Content) > 0 {
if v, err := s.Content.MarshalJSON(); err == nil {
b.Write([]byte(`"content":`))
b.Write(v)
if !empty {
b.Write([]byte{','})
}
empty = !writeNaturalLanguageProp(&b, "content", s.Content)
}
b.Write([]byte{'}'})
return b.Bytes(), nil
if !empty {
b.Write([]byte{'}'})
return b.Bytes(), nil
}
return nil, nil
}

View file

@ -3,6 +3,7 @@ package activitypub
import (
"reflect"
"testing"
"time"
)
func TestObjectNew(t *testing.T) {
@ -497,14 +498,269 @@ func TestObject_IsCollection(t *testing.T) {
t.Skip("TODO")
}
func TestObject_MarshalJSON(t *testing.T) {
t.Skip("TODO")
}
func TestSource_MarshalJSON(t *testing.T) {
t.Skip("TODO")
}
func TestActivityVocabularyType_MarshalJSON(t *testing.T) {
t.Skip("TODO")
}
func TestObject_MarshalJSON(t *testing.T) {
type fields struct {
ID ID
Type ActivityVocabularyType
Name NaturalLanguageValues
Attachment Item
AttributedTo Item
Audience ItemCollection
Content NaturalLanguageValues
Context Item
MediaType MimeType
EndTime time.Time
Generator Item
Icon Item
Image Item
InReplyTo Item
Location Item
Preview Item
Published time.Time
Replies Item
StartTime time.Time
Summary NaturalLanguageValues
Tag ItemCollection
Updated time.Time
URL LinkOrIRI
To ItemCollection
Bto ItemCollection
CC ItemCollection
BCC ItemCollection
Duration time.Duration
Likes Item
Shares Item
Source Source
}
tests := []struct {
name string
fields fields
want []byte
wantErr bool
}{
{
name: "Empty",
fields: fields{},
want: nil,
wantErr: false,
},
{
name: "JustID",
fields: fields{
ID: ID("example.com"),
},
want: []byte(`{"id":"example.com"}`),
wantErr: false,
},
{
name: "JustType",
fields: fields{
Type: ActivityVocabularyType("myType"),
},
want: []byte(`{"type":"myType"}`),
wantErr: false,
},
{
name: "JustOneName",
fields: fields{
Name: NaturalLanguageValues{
{Ref: NilLangRef, Value: "ana"},
},
},
want: []byte(`{"name":"ana"}`),
wantErr: false,
},
{
name: "MoreNames",
fields: fields{
Name: NaturalLanguageValues{
{Ref: "en", Value: "anna"},
{Ref: "fr", Value: "anne"},
},
},
want: []byte(`{"nameMap":{"en":"anna","fr":"anne"}}`),
wantErr: false,
},
{
name: "JustOneSummary",
fields: fields{
Summary: NaturalLanguageValues{
{Ref: NilLangRef, Value: "test summary"},
},
},
want: []byte(`{"summary":"test summary"}`),
wantErr: false,
},
{
name: "MoreSummaryEntries",
fields: fields{
Summary: NaturalLanguageValues{
{Ref: "en", Value: "test summary"},
{Ref: "fr", Value: "teste summary"},
},
},
want: []byte(`{"summaryMap":{"en":"test summary","fr":"teste summary"}}`),
wantErr: false,
},
{
name: "JustOneContent",
fields: fields{
Content: NaturalLanguageValues{
{Ref: NilLangRef, Value: "test content"},
},
},
want: []byte(`{"content":"test content"}`),
wantErr: false,
},
{
name: "MoreContentEntries",
fields: fields{
Content: NaturalLanguageValues{
{Ref: "en", Value: "test content"},
{Ref: "fr", Value: "teste content"},
},
},
want: []byte(`{"contentMap":{"en":"test content","fr":"teste content"}}`),
wantErr: false,
},
{
name: "MediaType",
fields: fields{
MediaType: MimeType("text/stupid"),
},
want: []byte(`{"mediaType":"text/stupid"}`),
wantErr: false,
},
{
name: "Source",
fields: fields{
Source: Source{
MediaType: MimeType("text/plain"),
Content: NaturalLanguageValues{},
},
},
want: []byte(`{"source":{"mediaType":"text/plain"}}`),
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
o := Object{
ID: tt.fields.ID,
Type: tt.fields.Type,
Name: tt.fields.Name,
Attachment: tt.fields.Attachment,
AttributedTo: tt.fields.AttributedTo,
Audience: tt.fields.Audience,
Content: tt.fields.Content,
Context: tt.fields.Context,
MediaType: tt.fields.MediaType,
EndTime: tt.fields.EndTime,
Generator: tt.fields.Generator,
Icon: tt.fields.Icon,
Image: tt.fields.Image,
InReplyTo: tt.fields.InReplyTo,
Location: tt.fields.Location,
Preview: tt.fields.Preview,
Published: tt.fields.Published,
Replies: tt.fields.Replies,
StartTime: tt.fields.StartTime,
Summary: tt.fields.Summary,
Tag: tt.fields.Tag,
Updated: tt.fields.Updated,
URL: tt.fields.URL,
To: tt.fields.To,
Bto: tt.fields.Bto,
CC: tt.fields.CC,
BCC: tt.fields.BCC,
Duration: tt.fields.Duration,
Likes: tt.fields.Likes,
Shares: tt.fields.Shares,
Source: tt.fields.Source,
}
got, err := o.MarshalJSON()
if (err != nil) != tt.wantErr {
t.Errorf("MarshalJSON() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("MarshalJSON() got = %s, want %s", got, tt.want)
}
})
}
}
func TestSource_MarshalJSON(t *testing.T) {
type fields struct {
Content NaturalLanguageValues
MediaType MimeType
}
tests := []struct {
name string
fields fields
want []byte
wantErr bool
}{
{
name: "Empty",
fields: fields{},
want: nil,
wantErr: false,
},
{
name: "MediaType",
fields: fields{
MediaType: MimeType("blank"),
},
want: []byte(`{"mediaType":"blank"}`),
wantErr: false,
},
{
name: "OneContentValue",
fields: fields{
Content: NaturalLanguageValues{
{Value: "test"},
},
},
want: []byte(`{"content":"test"}`),
wantErr: false,
},
{
name: "MultipleContentValues",
fields: fields{
Content: NaturalLanguageValues{
{
Ref: "en",
Value: "test",
},
{
Ref: "fr",
Value: "teste",
},
},
},
want: []byte(`{"contentMap":{"en":"test","fr":"teste"}}`),
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := Source{
Content: tt.fields.Content,
MediaType: tt.fields.MediaType,
}
got, err := s.MarshalJSON()
if (err != nil) != tt.wantErr {
t.Errorf("MarshalJSON() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("MarshalJSON() got = %s, want %s", got, tt.want)
}
})
}
}