From 4dc584b0d44fff136a015926181991bea61da43c Mon Sep 17 00:00:00 2001 From: Marius Orcsik Date: Tue, 24 Jul 2018 23:11:08 +0200 Subject: [PATCH] Added UnmarshalJSON methods for some types Add some tests --- activitypub/activity.go | 8 + activitypub/actors.go | 41 ++++- activitypub/collections.go | 32 +++- activitypub/inbox.go | 20 +++ activitypub/link.go | 17 +++ activitypub/marshalling.go | 229 ++++++++++++++++++++++++++++ activitypub/object.go | 59 +++++-- activitypub/outbox.go | 21 +++ activitypub/unmarshalling.go | 146 ++++++++++++++++++ activitypub/uri.go | 12 ++ tests/mocks/link_simple.json | 4 +- tests/mocks/object_with_url.json | 4 + tests/mocks/person_with_outbox.json | 13 ++ tests/unmarshalling_test.go | 73 ++++++++- 14 files changed, 658 insertions(+), 21 deletions(-) create mode 100644 activitypub/marshalling.go create mode 100644 activitypub/unmarshalling.go create mode 100644 tests/mocks/object_with_url.json create mode 100644 tests/mocks/person_with_outbox.json diff --git a/activitypub/activity.go b/activitypub/activity.go index d4efe17..0749581 100644 --- a/activitypub/activity.go +++ b/activitypub/activity.go @@ -837,3 +837,11 @@ func (d Dislike) IsObject() bool { func (d Dislike) IsLink() bool { return false } + +//// UnmarshalJSON +//func (a *Activity) UnmarshalJSON(data []byte) error { +// ob, err := unmarshal(data, *a) +// *a = ob.(Activity) +// +// return err +//} diff --git a/activitypub/actors.go b/activitypub/actors.go index 860ff80..fa7a5b3 100644 --- a/activitypub/actors.go +++ b/activitypub/actors.go @@ -1,6 +1,11 @@ package activitypub -import "time" +import ( + "fmt" + "time" + + "github.com/buger/jsonparser" +) // Actor Types const ( @@ -76,7 +81,7 @@ type Actor struct { // The notion of "context" used is intentionally vague. // The intended function is to serve as a means of grouping objects and activities that share a // common originating context or purpose. An example could be all activities relating to a common project or event. - //Context ObjectOrLink `jsonld:"_"` + Context ObjectOrLink `jsonld:"_"` // The date and time describing the actual or expected ending time of the object. // When used with an Activity object, for instance, the endTime property specifies the moment // the activity concluded or is expected to conclude. @@ -365,3 +370,35 @@ func (p Person) GetType() ActivityVocabularyType { func (p Person) GetLink() URI { return p.URL.(URI) } + +// UnmarshalJSON +func (a *Actor) UnmarshalJSON(data []byte) error { + a.ID = getAPObjectID(data) + a.Type = getAPType(data) + a.Name = getAPNaturalLanguageField(data, "name") + a.PreferredUsername = getAPNaturalLanguageField(data, "preferredUsername") + a.Content = getAPNaturalLanguageField(data, "content") + u := getURIField(data, "url") + if len(u) > 0 { + a.URL = u + } + + o := OutboxStream{} + v, _, _, err := jsonparser.Get(data, "outbox") + if err != nil { + fmt.Print(err) + } + o.UnmarshalJSON(v) + a.Outbox = o + + return nil +} + +func (p *Person) UnmarshalJSON(data []byte) error { + a := Actor(*p) + err := a.UnmarshalJSON(data) + + *p = Person(a) + + return err +} diff --git a/activitypub/collections.go b/activitypub/collections.go index 182cabb..e7817fc 100644 --- a/activitypub/collections.go +++ b/activitypub/collections.go @@ -1,6 +1,8 @@ package activitypub -import "time" +import ( + "time" +) var validCollectionTypes = [...]ActivityVocabularyType{CollectionType, OrderedCollectionType} @@ -315,6 +317,34 @@ func (o *OrderedCollection) IsObject() bool { return true } +// UnmarshalJSON +func (o *OrderedCollection) UnmarshalJSON(data []byte) error { + o.ID = getAPObjectID(data) + o.Type = getAPType(data) + o.Name = getAPNaturalLanguageField(data, "name") + o.Content = getAPNaturalLanguageField(data, "content") + u := getURIField(data, "url") + if len(u) > 0 { + o.URL = u + } + + return nil +} + +// UnmarshalJSON +func (c *Collection) UnmarshalJSON(data []byte) error { + c.ID = getAPObjectID(data) + c.Type = getAPType(data) + c.Name = getAPNaturalLanguageField(data, "name") + c.Content = getAPNaturalLanguageField(data, "content") + u := getURIField(data, "url") + if len(u) > 0 { + c.URL = u + } + + return nil +} + /* func (c *Collection) MarshalJSON() ([]byte, error) { return nil, nil diff --git a/activitypub/inbox.go b/activitypub/inbox.go index 21662f7..b8ee251 100644 --- a/activitypub/inbox.go +++ b/activitypub/inbox.go @@ -79,3 +79,23 @@ func (i Inbox) IsLink() bool { func (i Inbox) IsObject() bool { return true } + +// UnmarshalJSON +func (i *InboxStream) UnmarshalJSON(data []byte) error { + c := OrderedCollection(*i) + err := c.UnmarshalJSON(data) + + *i = InboxStream(c) + + return err +} + +// UnmarshalJSON +func (i *Inbox) UnmarshalJSON(data []byte) error { + c := OrderedCollection(*i) + err := c.UnmarshalJSON(data) + + *i = Inbox(c) + + return err +} diff --git a/activitypub/link.go b/activitypub/link.go index 86a47c4..7ab3ef6 100644 --- a/activitypub/link.go +++ b/activitypub/link.go @@ -103,3 +103,20 @@ func (m Mention) GetID() ObjectID { func (m Mention) GetType() ActivityVocabularyType { return m.Type } + +// UnmarshalJSON +func (l *Link) UnmarshalJSON(data []byte) error { + l.ID = getAPObjectID(data) + l.Type = getAPType(data) + l.MediaType = getAPMimeType(data) + l.Name = getAPNaturalLanguageField(data, "name") + l.HrefLang = getAPLangRefField(data, "hrefLang") + u := getURIField(data, "href") + if len(u) > 0 { + l.Href = u + } + + //fmt.Printf("%s\n %#v", data, l) + + return nil +} diff --git a/activitypub/marshalling.go b/activitypub/marshalling.go new file mode 100644 index 0000000..87e6455 --- /dev/null +++ b/activitypub/marshalling.go @@ -0,0 +1,229 @@ +package activitypub + +import ( + "fmt" +) + +func getAPObjectByType(typ ActivityVocabularyType) (interface{}, error) { + var ret interface{} + var err error + + switch typ { + case ObjectType: + ret = Object{} + o := ret.(Object) + o.Type = typ + case LinkType: + ret = Link{} + o := ret.(Link) + o.Type = typ + case ActivityType: + ret = Activity{} + o := ret.(Activity) + o.Type = typ + case IntransitiveActivityType: + ret = IntransitiveActivity{} + o := ret.(IntransitiveActivity) + o.Type = typ + case ActorType: + ret = Actor{} + o := ret.(Actor) + o.Type = typ + case CollectionType: + ret = Collection{} + o := ret.(Collection) + o.Type = typ + case OrderedCollectionType: + ret = Link{} + o := ret.(Link) + o.Type = typ + case ArticleType: + ret = Object{} + o := ret.(Object) + o.Type = typ + case AudioType: + ret = Object{} + o := ret.(Object) + o.Type = typ + case DocumentType: + ret = Object{} + o := ret.(Object) + o.Type = typ + case EventType: + o := Object{} + o.Type = typ + case ImageType: + ret = Object{} + o := ret.(Object) + o.Type = typ + case NoteType: + ret = Object{} + o := ret.(Object) + o.Type = typ + ret = o + case PageType: + ret = Object{} + o := ret.(Object) + o.Type = typ + case PlaceType: + ret = Object{} + o := ret.(Object) + o.Type = typ + case ProfileType: + ret = Object{} + o := ret.(Object) + o.Type = typ + case RelationshipType: + ret = Object{} + o := ret.(Object) + o.Type = typ + case TombstoneType: + ret = Object{} + o := ret.(Object) + o.Type = typ + case VideoType: + ret = Object{} + o := ret.(Object) + o.Type = typ + case MentionType: + ret = Mention{} + o := ret.(Mention) + o.Type = typ + case ApplicationType: + ret = Application{} + o := ret.(Application) + o.Type = typ + case GroupType: + ret = Group{} + o := ret.(Group) + o.Type = typ + case OrganizationType: + ret = Organization{} + o := ret.(Organization) + o.Type = typ + case PersonType: + ret = Person{} + o := ret.(Person) + o.Type = typ + case ServiceType: + ret = Service{} + o := ret.(Service) + o.Type = typ + case AcceptType: + ret = Accept{} + o := ret.(Accept) + o.Type = typ + case AddType: + ret = Add{} + o := ret.(Add) + o.Type = typ + case AnnounceType: + ret = Announce{} + o := ret.(Announce) + o.Type = typ + case ArriveType: + ret = Arrive{} + o := ret.(Arrive) + o.Type = typ + case BlockType: + ret = Block{} + o := ret.(Block) + o.Type = typ + case CreateType: + ret = Create{} + o := ret.(Create) + o.Type = typ + case DeleteType: + ret = Delete{} + o := ret.(Delete) + o.Type = typ + case DislikeType: + ret = Dislike{} + o := ret.(Dislike) + o.Type = typ + case FlagType: + ret = Flag{} + o := ret.(Flag) + o.Type = typ + case FollowType: + ret = Follow{} + o := ret.(Follow) + o.Type = typ + case IgnoreType: + ret = Ignore{} + o := ret.(Ignore) + o.Type = typ + case InviteType: + ret = Invite{} + o := ret.(Invite) + o.Type = typ + case JoinType: + ret = Join{} + o := ret.(Join) + o.Type = typ + case LeaveType: + ret = Leave{} + o := ret.(Leave) + o.Type = typ + case LikeType: + ret = Like{} + o := ret.(Like) + o.Type = typ + case ListenType: + ret = Listen{} + o := ret.(Listen) + o.Type = typ + case MoveType: + ret = Move{} + o := ret.(Move) + o.Type = typ + case OfferType: + ret = Offer{} + o := ret.(Offer) + o.Type = typ + case QuestionType: + ret = Question{} + o := ret.(Question) + o.Type = typ + case RejectType: + ret = Reject{} + o := ret.(Reject) + o.Type = typ + case ReadType: + ret = Read{} + o := ret.(Read) + o.Type = typ + case RemoveType: + ret = Remove{} + o := ret.(Remove) + o.Type = typ + case TentativeRejectType: + ret = TentativeReject{} + o := ret.(TentativeReject) + o.Type = typ + case TentativeAcceptType: + ret = TentativeAccept{} + o := ret.(TentativeAccept) + o.Type = typ + case TravelType: + ret = Travel{} + o := ret.(Travel) + o.Type = typ + case UndoType: + ret = Undo{} + o := ret.(Undo) + o.Type = typ + case UpdateType: + ret = Update{} + o := ret.(Update) + o.Type = typ + case ViewType: + ret = View{} + o := ret.(View) + o.Type = typ + default: + ret = nil + err = fmt.Errorf("%q unrecognized ActivityPub type", typ) + } + return ret, err +} diff --git a/activitypub/object.go b/activitypub/object.go index 6fda5bf..641782c 100644 --- a/activitypub/object.go +++ b/activitypub/object.go @@ -86,7 +86,7 @@ type ( ActivityVocabularyType string // ActivityObject is a subtype of Object that describes some form of action that may happen, // is currently happening, or has already happened - ActivityObject interface{ + ActivityObject interface { GetID() ObjectID } // ObjectOrLink describes an object of any kind. @@ -105,7 +105,10 @@ type ( GetLink() URI } // ImageOrLink is an interface that Image and Link structs implement - ImageOrLink interface{} + ImageOrLink interface { + ObjectOrLink + LinkOrURI + } // MimeType is the type for MIME types MimeType string // LangRef is the type for a language reference, should be ISO 639-1 language specifier. @@ -135,6 +138,14 @@ func (n NaturalLanguageValue) MarshalJSON() ([]byte, error) { return json.Marshal(map[LangRef]string(n)) } +// MarshalText serializes the NaturalLanguageValue into Text +func (n NaturalLanguageValue) MarshalText() ([]byte, error) { + for _, v := range n { + return []byte(fmt.Sprintf("%q", v)), nil + } + return nil, nil +} + // Append is syntactic sugar for resizing the NaturalLanguageValue map // and appending an element func (n *NaturalLanguageValue) Append(lang LangRef, value string) error { @@ -156,8 +167,17 @@ func (l *LangRef) UnmarshalJSON(data []byte) error { return nil } +// UnmarshalText tries to load the NaturalLanguage array from the incoming Text value +func (l *LangRef) UnmarshalText(data []byte) error { + *l = LangRef(data[1 : len(data)-1]) + return nil +} + // UnmarshalJSON tries to load the NaturalLanguage array from the incoming json value func (n *NaturalLanguageValue) UnmarshalJSON(data []byte) error { + if data == nil || len(data) == 0 { + return fmt.Errorf("invalid incoming bytes") + } if data[0] == '"' { // a quoted string - loading it to c.URL if data[len(data)-1] != '"' { @@ -180,11 +200,23 @@ func (n *NaturalLanguageValue) UnmarshalJSON(data []byte) error { return nil } +// UnmarshalText tries to load the NaturalLanguage array from the incoming Text value +func (n *NaturalLanguageValue) UnmarshalText(data []byte) error { + if data[0] == '"' { + // a quoted string - loading it to c.URL + if data[len(data)-1] != '"' { + return fmt.Errorf("invalid string value when unmarshalling %T value", n) + } + n.Append(LangRef("-"), string(data[1:len(data)-1])) + } + return nil +} + // Describes an object of any kind. // The Activity Pub Object type serves as the base type for most of the other kinds of objects defined in the Activity Vocabulary, // including other Core types such as Activity, IntransitiveActivity, Collection and OrderedCollection. type Object struct { - // Provides the globally unique identifier for anActivity Pub Object or Link. + // Provides the globally unique identifier for anActivity Pub Object or Link. ID ObjectID `jsonld:"id,omitempty"` // Identifies the Activity Pub Object or Link type. Multiple values may be specified. Type ActivityVocabularyType `jsonld:"type,omitempty"` @@ -375,9 +407,18 @@ func (c *ContentType) UnmarshalJSON(data []byte) error { return nil } -//func (o *Object) UnmarshalJSON(data []byte) error { -// //_o, err := unmarshalJSONFromType(data, ObjectType) -// -// //*o = _o.(Object) -// return nil -//} +// UnmarshalJSON +func (o *Object) UnmarshalJSON(data []byte) error { + o.ID = getAPObjectID(data) + o.Type = getAPType(data) + o.Name = getAPNaturalLanguageField(data, "name") + o.Content = getAPNaturalLanguageField(data, "content") + u := getURIField(data, "url") + if len(u) > 0 { + o.URL = u + } + + //fmt.Printf("%s\n %#v", data, o) + + return nil +} diff --git a/activitypub/outbox.go b/activitypub/outbox.go index 53dbfcd..3a7b7b4 100644 --- a/activitypub/outbox.go +++ b/activitypub/outbox.go @@ -52,6 +52,7 @@ func (o OutboxStream) GetType() ActivityVocabularyType { func (o OutboxStream) IsLink() bool { return false } + // IsObject returns true for a OutboxStream object func (o OutboxStream) IsObject() bool { return true @@ -76,3 +77,23 @@ func (o Outbox) IsLink() bool { func (o Outbox) IsObject() bool { return true } + +// UnmarshalJSON +func (o *OutboxStream) UnmarshalJSON(data []byte) error { + c := OrderedCollection(*o) + err := c.UnmarshalJSON(data) + + *o = OutboxStream(c) + + return err +} + +// UnmarshalJSON +func (o *Outbox) UnmarshalJSON(data []byte) error { + c := OrderedCollection(*o) + err := c.UnmarshalJSON(data) + + *o = Outbox(c) + + return err +} diff --git a/activitypub/unmarshalling.go b/activitypub/unmarshalling.go new file mode 100644 index 0000000..8dd9f61 --- /dev/null +++ b/activitypub/unmarshalling.go @@ -0,0 +1,146 @@ +package activitypub + +import ( + "encoding" + "encoding/json" + "reflect" + "strings" + + "github.com/buger/jsonparser" +) + +var ( + apUnmarshalerType = reflect.TypeOf(new(ObjectOrLink)).Elem() + unmarshalerType = reflect.TypeOf(new(json.Unmarshaler)).Elem() + textUnmarshalerType = reflect.TypeOf(new(encoding.TextUnmarshaler)).Elem() +) + +type mockObj map[string]json.RawMessage + +func getType(j json.RawMessage) ActivityVocabularyType { + mock := make(mockObj, 0) + json.Unmarshal([]byte(j), &mock) + + for key, val := range mock { + if strings.ToLower(key) == "type" { + return ActivityVocabularyType(strings.Trim(string(val), "\"")) + } + } + return "" +} + +func getAPObjectID(data []byte) ObjectID { + i, err := jsonparser.GetString(data, "id") + if err != nil { + return ObjectID("") + } + return ObjectID(i) +} + +func getAPType(data []byte) ActivityVocabularyType { + t, err := jsonparser.GetString(data, "type") + typ := ActivityVocabularyType(t) + if err != nil { + return ActivityVocabularyType("") + } + return typ +} + +func getAPMimeType(data []byte) MimeType { + t, err := jsonparser.GetString(data, "mediaType") + if err != nil { + return MimeType("") + } + return MimeType(t) +} + +func getAPNaturalLanguageField(data []byte, prop string) NaturalLanguageValue { + n := NaturalLanguageValue{} + val, typ, _, err := jsonparser.Get(data, prop) + if err != nil { + return NaturalLanguageValue(nil) + } + switch typ { + case jsonparser.Object: + jsonparser.ObjectEach(data, func(key []byte, value []byte, dataType jsonparser.ValueType, offset int) error { + vv, err := jsonparser.GetString(val, string(key)) + n.Append(LangRef(key), vv) + return err + }, prop) + case jsonparser.String: + n.Append("-", string(val)) + } + + return n +} +func getURIField(data []byte, prop string) URI { + val, err := jsonparser.GetString(data, prop) + if err != nil { + return URI("") + } + return URI(val) +} + +func getAPLangRefField(data []byte, prop string) LangRef { + val, err := jsonparser.GetString(data, prop) + if err != nil { + return LangRef("") + } + return LangRef(val) +} + +/* +func unmarshal(data []byte, a interface{}) (interface{}, error) { + ta := make(mockObj, 0) + err := jsonld.Unmarshal(data, &ta) + if err != nil { + return nil, err + } + + typ := reflect.TypeOf(a) + val := reflect.ValueOf(a) + + if typ.Kind() == reflect.Ptr { + typ = typ.Elem() + val = val.Elem() + } + + for i := 0; i < typ.NumField(); i++ { + cField := typ.Field(i) + cValue := val.Field(i) + cTag := cField.Tag + tag, _ := jsonld.LoadJSONLdTag(cTag) + + var vv reflect.Value + for key, j := range ta { + if j == nil { + continue + } + if key == tag.Name { + if cField.Type.Implements(textUnmarshalerType) { + m, _ := cValue.Interface().(encoding.TextUnmarshaler) + m.UnmarshalText(j) + vv = reflect.ValueOf(m) + } + if cField.Type.Implements(unmarshalerType) { + m, _ := cValue.Interface().(json.Unmarshaler) + m.UnmarshalJSON(j) + vv = reflect.ValueOf(m) + } + if cField.Type.Implements(apUnmarshalerType) { + o := getAPObjectByType(getType(j)) + if o != nil { + jsonld.Unmarshal([]byte(j), o) + vv = reflect.ValueOf(o) + } + } + } + if vv.CanAddr() { + cValue.Set(vv) + fmt.Printf("\n\nReflected %q %q => %#v\n\n%#v\n", cField.Name, cField.Type, vv, tag.Name) + } + } + } + return a, nil +} +*/ diff --git a/activitypub/uri.go b/activitypub/uri.go index 4ef96c2..211a369 100644 --- a/activitypub/uri.go +++ b/activitypub/uri.go @@ -43,3 +43,15 @@ func (i *IRI) UnmarshalJSON(s []byte) error { *i = IRI(strings.Trim(string(s), "\"")) return nil } + +// UnmarshalText +func (u URI) UnmarshalText(s []byte) error { + u = URI(strings.Trim(string(s), "\"")) + return nil +} + +// UnmarshalText +func (i IRI) UnmarshalText(s []byte) error { + i = IRI(strings.Trim(string(s), "\"")) + return nil +} diff --git a/tests/mocks/link_simple.json b/tests/mocks/link_simple.json index a173834..bfed0cf 100644 --- a/tests/mocks/link_simple.json +++ b/tests/mocks/link_simple.json @@ -2,7 +2,7 @@ "@context": "https://www.w3.org/ns/activitystreams", "type": "Link", "href": "http://example.org/abc", - "hreflang": "en", + "hrefLang": "en", "mediaType": "text/html", "name": "An example link" -} \ No newline at end of file +} diff --git a/tests/mocks/object_with_url.json b/tests/mocks/object_with_url.json new file mode 100644 index 0000000..23b36b3 --- /dev/null +++ b/tests/mocks/object_with_url.json @@ -0,0 +1,4 @@ +{ + "@context":"https://www.w3.org/ns/activitystreams", + "url":"http://littr.git/api/accounts/system" +} diff --git a/tests/mocks/person_with_outbox.json b/tests/mocks/person_with_outbox.json new file mode 100644 index 0000000..a2e3bf7 --- /dev/null +++ b/tests/mocks/person_with_outbox.json @@ -0,0 +1,13 @@ +{ + "@context": "https://www.w3.org/ns/activitystreams", + "id": "http://example.com/accounts/ana", + "type": "Person", + "name": "ana", + "url": "http://example.com/accounts/ana", + "outbox": { + "id": "http://example.com/accounts/ana/outbox", + "type": "OrderedCollection", + "url": "http://example.com/outbox" + }, + "preferredUsername": "Ana" +} diff --git a/tests/unmarshalling_test.go b/tests/unmarshalling_test.go index 5c1dfbf..cf5d526 100644 --- a/tests/unmarshalling_test.go +++ b/tests/unmarshalling_test.go @@ -46,6 +46,14 @@ var allTests = tests{ }, }, }, + "object_with_url": testPair{ + path: "./mocks/object_with_url.json", + expected: true, + blank: &a.Object{}, + result: &a.Object{ + URL: a.URI("http://littr.git/api/accounts/system"), + }, + }, "object_simple": testPair{ path: "./mocks/object_simple.json", expected: true, @@ -60,23 +68,59 @@ var allTests = tests{ }, //"activity_simple": testPair{ // path: "./mocks/activity_simple.json", - // expected: true, + // expected: false, // blank: &a.Activity{}, // result: &a.Activity{ // Type: a.ActivityType, // Summary: a.NaturalLanguageValue{a.LangRef("-"): "Sally did something to a note"}, + // Actor: a.Actor(a.Person{ + // Type: a.PersonType, + // Name: a.NaturalLanguageValue{ + // a.LangRef("-"): "Sally", + // }, + // }), + // Object: a.Object{ + // Type: a.NoteType, + // Name: a.NaturalLanguageValue{ + // a.LangRef("-"): "A Note", + // }, + // }, // }, //}, + "person_with_outbox": testPair{ + path: "./mocks/person_with_outbox.json", + expected: true, + blank: &a.Person{}, + result: &a.Person{ + ID: a.ObjectID("http://example.com/accounts/ana"), + Type: a.PersonType, + Name: a.NaturalLanguageValue{ + a.LangRef("-"): "ana", + }, + PreferredUsername: a.NaturalLanguageValue{ + a.LangRef("-"): "Ana", + }, + URL: a.URI("http://example.com/accounts/ana"), + Outbox: a.OutboxStream{ + ID: a.ObjectID("http://example.com/accounts/ana/outbox"), + Type: a.OrderedCollectionType, + URL: a.URI("http://example.com/outbox"), + }, + }, + }, } -func getFileContents(path string) []byte { - f, _ := os.Open(path) +func getFileContents(path string) ([]byte, error) { + f, err := os.Open(path) + if err != nil { + return nil, err + } data := make([]byte, 512) io.ReadFull(f, data) data = bytes.Trim(data, "\x00") - return data + return data, nil } func Test_ActivityPubUnmarshall(t *testing.T) { @@ -92,13 +136,17 @@ func Test_ActivityPubUnmarshall(t *testing.T) { } for k, pair := range allTests { - data := getFileContents(pair.path) - + var data []byte + data, err = getFileContents(pair.path) + if err != nil { + f("Error: %s for %s", err, pair.path) + continue + } object := pair.blank err = j.Unmarshal(data, object) if err != nil { - f("Error: %s", err) + f("Error: %s for %s", err, data) continue } expLbl := "" @@ -107,8 +155,19 @@ func Test_ActivityPubUnmarshall(t *testing.T) { } if pair.expected != reflect.DeepEqual(object, pair.result) { f("\n%#v\n should %sequal to\n%#v", object, expLbl, pair.result) + oj, e1 := j.Marshal(object) + if e1 != nil { + f(e1.Error()) + } + tj, e2 := j.Marshal(pair.result) + if e2 != nil { + f(e2.Error()) + } + + f("\n%s\n should %sequal to\n%s", oj, expLbl, tj) continue } + //fmt.Printf("%#v", object) if err == nil { fmt.Printf(" --- %s: %s\n %s\n", "PASS", k, pair.path) }