• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

go-ap / activitypub / #5

08 Feb 2026 11:03AM UTC coverage: 43.282% (-0.03%) from 43.31%
#5

push

sourcehut

mariusor
Updated errors module with invalid content type error

3276 of 7569 relevant lines covered (43.28%)

98.75 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

55.0
/decoding_json.go
1
package activitypub
2

3
import (
4
        "encoding"
5
        "encoding/json"
6
        "fmt"
7
        "net/url"
8
        "reflect"
9
        "strings"
10
        "time"
11

12
        "github.com/valyala/fastjson"
13
)
14

15
var (
16
        apUnmarshalerType   = reflect.TypeOf(new(Item)).Elem()
17
        unmarshalerType     = reflect.TypeOf(new(json.Unmarshaler)).Elem()
18
        textUnmarshalerType = reflect.TypeOf(new(encoding.TextUnmarshaler)).Elem()
19
)
20

21
// ItemTyperFunc will return an instance of a struct that implements activitypub.Item
22
// The default for this package is GetItemByType but can be overwritten
23
var ItemTyperFunc TyperFn = GetItemByType
24

25
// JSONItemUnmarshal can be set externally to populate a custom object based on its type
26
var JSONItemUnmarshal JSONUnmarshalerFn = nil
27

28
// IsNotEmpty checks if an object is empty
29
var IsNotEmpty NotEmptyCheckerFn = NotEmpty
30

31
// TyperFn is the type of the function which returns an Item struct instance
32
// for a specific ActivityVocabularyType
33
type TyperFn func(Typer) (Item, error)
34

35
// JSONUnmarshalerFn is the type of the function that will load the data from a fastjson.Value into an Item
36
// that the current package doesn't know about.
37
type JSONUnmarshalerFn func(Typer, *fastjson.Value, Item) error
38

39
// NotEmptyCheckerFn is the type of the function that checks if an object is empty
40
type NotEmptyCheckerFn func(Item) bool
41

42
func JSONGetID(val *fastjson.Value) ID {
44✔
43
        i := val.Get("id").GetStringBytes()
44✔
44
        return ID(i)
44✔
45
}
44✔
46

47
func JSONGetTypes(val *fastjson.Value) Typer {
78✔
48
        value := val.Get("type")
78✔
49
        if value == nil {
128✔
50
                return nil
50✔
51
        }
50✔
52
        switch value.Type() {
28✔
53
        case fastjson.TypeString:
26✔
54
                return ActivityVocabularyType(value.GetStringBytes())
26✔
55
        case fastjson.TypeArray:
2✔
56
                valArr, err := value.Array()
2✔
57
                if err != nil {
2✔
58
                        return ActivityVocabularyTypes{}
×
59
                }
×
60
                types := make(ActivityVocabularyTypes, len(valArr))
2✔
61
                for i, v := range valArr {
6✔
62
                        types[i] = ActivityVocabularyType(v.GetStringBytes())
4✔
63
                }
4✔
64
                return types
2✔
65
        default:
×
66
                return nil
×
67
        }
68
}
69

70
func JSONGetMimeType(val *fastjson.Value, prop string) MimeType {
44✔
71
        if !val.Exists(prop) {
86✔
72
                return ""
42✔
73
        }
42✔
74
        t := val.GetStringBytes(prop)
2✔
75
        return MimeType(t)
2✔
76
}
77

78
func JSONGetInt(val *fastjson.Value, prop string) int64 {
12✔
79
        if !val.Exists(prop) {
22✔
80
                return 0
10✔
81
        }
10✔
82
        i := val.Get(prop).GetInt64()
2✔
83
        return i
2✔
84
}
85

86
func JSONGetFloat(val *fastjson.Value, prop string) float64 {
×
87
        if !val.Exists(prop) {
×
88
                return 0.0
×
89
        }
×
90
        f := val.Get(prop).GetFloat64()
×
91
        return f
×
92
}
93

94
func JSONGetString(val *fastjson.Value, prop string) string {
×
95
        if !val.Exists(prop) {
×
96
                return ""
×
97
        }
×
98
        s := val.Get(prop).GetStringBytes()
×
99
        return string(s)
×
100
}
101

102
func JSONGetBytes(val *fastjson.Value, prop string) []byte {
×
103
        if !val.Exists(prop) {
×
104
                return nil
×
105
        }
×
106
        s := val.Get(prop).GetStringBytes()
×
107
        return s
×
108
}
109

110
func JSONGetBoolean(val *fastjson.Value, prop string) bool {
×
111
        if !val.Exists(prop) {
×
112
                return false
×
113
        }
×
114
        t, _ := val.Get(prop).Bool()
×
115
        return t
×
116
}
117

118
func JSONGetNaturalLanguageField(val *fastjson.Value, prop string) NaturalLanguageValues {
142✔
119
        n := make(NaturalLanguageValues)
142✔
120
        if val == nil {
142✔
121
                return n
×
122
        }
×
123
        v := val.Get(prop)
142✔
124
        if v == nil {
282✔
125
                return nil
140✔
126
        }
140✔
127
        switch v.Type() {
2✔
128
        case fastjson.TypeObject:
×
129
                ob, _ := v.Object()
×
130
                ob.Visit(func(key []byte, v *fastjson.Value) {
×
131
                        cont := Content{}
×
132
                        ref := MakeRef(key)
×
133
                        if err := cont.UnmarshalJSON(v.GetStringBytes()); err == nil {
×
134
                                if ref != NilLangRef || len(cont) > 0 {
×
135
                                        n[ref] = cont
×
136
                                }
×
137
                        }
138
                })
139
        case fastjson.TypeString:
2✔
140
                if raw := v.GetStringBytes(); len(raw) > 0 {
4✔
141
                        n[DefaultLang] = raw
2✔
142
                }
2✔
143
        }
144

145
        return n
2✔
146
}
147

148
func JSONGetTime(val *fastjson.Value, prop string) time.Time {
176✔
149
        t := time.Time{}
176✔
150
        if val == nil {
176✔
151
                return t
×
152
        }
×
153

154
        if str := val.Get(prop).GetStringBytes(); len(str) > 0 {
182✔
155
                t.UnmarshalText(str)
6✔
156
                return t.UTC()
6✔
157
        }
6✔
158
        return t
170✔
159
}
160

161
func JSONGetDuration(val *fastjson.Value, prop string) time.Duration {
44✔
162
        if str := val.Get(prop).GetStringBytes(); len(str) > 0 {
44✔
163
                // TODO(marius): this needs to be replaced to be compatible with xsd:duration
×
164
                d, _ := time.ParseDuration(string(str))
×
165
                return d
×
166
        }
×
167
        return 0
44✔
168
}
169

170
func JSONGetPublicKey(val *fastjson.Value, prop string) PublicKey {
10✔
171
        key := PublicKey{}
10✔
172
        if val == nil {
10✔
173
                return key
×
174
        }
×
175
        val = val.Get(prop)
10✔
176
        if val == nil {
20✔
177
                return key
10✔
178
        }
10✔
179
        JSONLoadPublicKey(val, &key)
×
180
        return key
×
181
}
182

183
func JSONItemsFn(val *fastjson.Value) (Item, error) {
2✔
184
        if val.Type() == fastjson.TypeArray {
4✔
185
                it := val.GetArray()
2✔
186
                items := make(ItemCollection, 0)
2✔
187
                for _, v := range it {
6✔
188
                        if it, _ := JSONLoadItem(v); it != nil {
8✔
189
                                _ = items.Append(it)
4✔
190
                        }
4✔
191
                }
192
                return items, nil
2✔
193
        }
194
        return JSONLoadItem(val)
×
195
}
196

197
func looksLikeALink(val *fastjson.Value) bool {
2✔
198
        return val.Exists("href")
2✔
199
}
2✔
200

201
func JSONLoadItem(val *fastjson.Value) (Item, error) {
28✔
202
        typ := JSONGetTypes(val)
28✔
203
        if typ == nil {
44✔
204
                typ = NilType
16✔
205
        }
16✔
206

207
        if EmptyTypes(typ.AsTypes()...) && val.Type() == fastjson.TypeString {
42✔
208
                // try to see if it's an IRI
14✔
209
                if i, ok := asIRI(val); ok {
28✔
210
                        return i, nil
14✔
211
                }
14✔
212
        }
213
        i, err := ItemTyperFunc(typ)
14✔
214
        if err != nil || IsNil(i) {
14✔
215
                return nil, nil
×
216
        }
×
217
        var empty = func(i Item) bool { return !IsNotEmpty(i) }
28✔
218

219
        // NOTE(marius): see note for the [GetItemByType] switch, the same applies here.
220
        switch {
14✔
221
        case CollectionType.Match(typ):
×
222
                err = OnCollection(i, func(c *Collection) error {
×
223
                        return JSONLoadCollection(val, c)
×
224
                })
×
225
        case OrderedCollectionType.Match(typ):
2✔
226
                err = OnOrderedCollection(i, func(c *OrderedCollection) error {
4✔
227
                        return JSONLoadOrderedCollection(val, c)
2✔
228
                })
2✔
229
        case CollectionPageType.Match(typ):
×
230
                err = OnCollectionPage(i, func(p *CollectionPage) error {
×
231
                        return JSONLoadCollectionPage(val, p)
×
232
                })
×
233
        case OrderedCollectionPageType.Match(typ):
×
234
                err = OnOrderedCollectionPage(i, func(p *OrderedCollectionPage) error {
×
235
                        return JSONLoadOrderedCollectionPage(val, p)
×
236
                })
×
237
        case PlaceType.Match(typ):
×
238
                err = OnPlace(i, func(p *Place) error {
×
239
                        return JSONLoadPlace(val, p)
×
240
                })
×
241
        case ProfileType.Match(typ):
×
242
                err = OnProfile(i, func(p *Profile) error {
×
243
                        return JSONLoadProfile(val, p)
×
244
                })
×
245
        case RelationshipType.Match(typ):
×
246
                err = OnRelationship(i, func(r *Relationship) error {
×
247
                        return JSONLoadRelationship(val, r)
×
248
                })
×
249
        case TombstoneType.Match(typ):
×
250
                err = OnTombstone(i, func(t *Tombstone) error {
×
251
                        return JSONLoadTombstone(val, t)
×
252
                })
×
253
        case QuestionType.Match(typ):
×
254
                err = OnQuestion(i, func(q *Question) error {
×
255
                        return JSONLoadQuestion(val, q)
×
256
                })
×
257
        case NilType.Match(typ):
2✔
258
                if looksLikeALink(val) {
2✔
259
                        // NOTE(marius): this handles Links without a type
×
260
                        return JSONLoadLink(val)
×
261
                }
×
262
                err = OnObject(i, func(ob *Object) error {
4✔
263
                        // NOTE(marius): this handles Tags which usually don't have types
2✔
264
                        return JSONLoadObject(val, ob)
2✔
265
                })
2✔
266
        case ActivityVocabularyTypes{ObjectType, ArticleType, AudioType, DocumentType, EventType, ImageType, NoteType, PageType, VideoType}.Match(typ):
4✔
267
                err = OnObject(i, func(ob *Object) error {
8✔
268
                        return JSONLoadObject(val, ob)
4✔
269
                })
4✔
270
        case ActivityVocabularyTypes{LinkType, MentionType}.Match(typ):
×
271
                // NOTE(marius): if we have a clear link type, we override
×
272
                i = new(Link)
×
273
                err = OnLink(i, func(l *Link) error {
×
274
                        return jsonLoadToLink(val, l)
×
275
                })
×
276
        case ActivityVocabularyTypes{ActivityType, AcceptType, AddType, AnnounceType, BlockType, CreateType, DeleteType, DislikeType,
277
                FlagType, FollowType, IgnoreType, InviteType, JoinType, LeaveType, LikeType, ListenType, MoveType, OfferType,
278
                RejectType, ReadType, RemoveType, TentativeRejectType, TentativeAcceptType, UndoType, UpdateType, ViewType}.Match(typ):
6✔
279
                err = OnActivity(i, func(act *Activity) error {
12✔
280
                        return JSONLoadActivity(val, act)
6✔
281
                })
6✔
282
        case ActivityVocabularyTypes{IntransitiveActivityType, ArriveType, TravelType}.Match(typ):
×
283
                err = OnIntransitiveActivity(i, func(act *IntransitiveActivity) error {
×
284
                        return JSONLoadIntransitiveActivity(val, act)
×
285
                })
×
286
        case ActivityVocabularyTypes{ActorType, ApplicationType, GroupType, OrganizationType, PersonType, ServiceType}.Match(typ):
×
287
                err = OnActor(i, func(a *Actor) error {
×
288
                        return JSONLoadActor(val, a)
×
289
                })
×
290
        default:
×
291
                if JSONItemUnmarshal == nil {
×
292
                        return nil, fmt.Errorf("unable to unmarshal custom type %s, you need to set a correct function for JSONItemUnmarshal", typ)
×
293
                }
×
294
                err = JSONItemUnmarshal(typ, val, i)
×
295
        }
296
        if err != nil {
14✔
297
                return nil, err
×
298
        }
×
299
        if empty(i) {
16✔
300
                return nil, nil
2✔
301
        }
2✔
302

303
        return i, nil
12✔
304
}
305

306
func JSONUnmarshalToItem(val *fastjson.Value) Item {
14✔
307
        var (
14✔
308
                i   Item
14✔
309
                err error
14✔
310
        )
14✔
311
        switch val.Type() {
14✔
312
        case fastjson.TypeArray:
2✔
313
                i, err = JSONItemsFn(val)
2✔
314
        case fastjson.TypeObject:
10✔
315
                i, err = JSONLoadItem(val)
10✔
316
        case fastjson.TypeString:
2✔
317
                if iri, ok := asIRI(val); ok {
4✔
318
                        // try to see if it's an IRI
2✔
319
                        i = iri
2✔
320
                }
2✔
321
        }
322
        if err != nil {
14✔
323
                return nil
×
324
        }
×
325
        return i
14✔
326
}
327

328
func asIRI(val *fastjson.Value) (IRI, bool) {
38✔
329
        if val == nil {
38✔
330
                return NilIRI, true
×
331
        }
×
332
        s := strings.Trim(val.String(), `"`)
38✔
333
        u, err := url.ParseRequestURI(s)
38✔
334
        if err == nil && len(u.Scheme) > 0 && len(u.Host) > 0 {
76✔
335
                // try to see if it's an IRI
38✔
336
                return IRI(s), true
38✔
337
        }
38✔
338
        return EmptyIRI, false
×
339
}
340

341
func JSONGetItem(val *fastjson.Value, prop string) Item {
716✔
342
        if val == nil {
716✔
343
                return nil
×
344
        }
×
345
        if val = val.Get(prop); val == nil {
1,418✔
346
                return nil
702✔
347
        }
702✔
348
        switch val.Type() {
14✔
349
        case fastjson.TypeString:
14✔
350
                if i, ok := asIRI(val); ok {
28✔
351
                        // try to see if it's an IRI
14✔
352
                        return i
14✔
353
                }
14✔
354
        case fastjson.TypeArray:
×
355
                it, _ := JSONItemsFn(val)
×
356
                return it
×
357
        case fastjson.TypeObject:
×
358
                it, _ := JSONLoadItem(val)
×
359
                return it
×
360
        case fastjson.TypeNumber:
×
361
                fallthrough
×
362
        case fastjson.TypeNull:
×
363
                fallthrough
×
364
        default:
×
365
                return nil
×
366
        }
367
        return nil
×
368
}
369

370
func JSONGetURIItem(val *fastjson.Value, prop string) Item {
44✔
371
        if val == nil {
44✔
372
                return nil
×
373
        }
×
374
        if val = val.Get(prop); val == nil {
88✔
375
                return nil
44✔
376
        }
44✔
377
        switch val.Type() {
×
378
        case fastjson.TypeObject:
×
379
                if it, _ := JSONLoadItem(val); it != nil {
×
380
                        return it
×
381
                }
×
382
        case fastjson.TypeArray:
×
383
                if it, _ := JSONItemsFn(val); it != nil {
×
384
                        return it
×
385
                }
×
386
        case fastjson.TypeString:
×
387
                return IRI(val.GetStringBytes())
×
388
        }
389

390
        return nil
×
391
}
392

393
func JSONGetItems(val *fastjson.Value, prop string) ItemCollection {
284✔
394
        if val == nil {
284✔
395
                return nil
×
396
        }
×
397
        if val = val.Get(prop); val == nil {
558✔
398
                return nil
274✔
399
        }
274✔
400

401
        it := make(ItemCollection, 0)
10✔
402
        switch val.Type() {
10✔
403
        case fastjson.TypeArray:
10✔
404
                for _, v := range val.GetArray() {
24✔
405
                        if i, _ := JSONLoadItem(v); i != nil {
28✔
406
                                _ = it.Append(i)
14✔
407
                        }
14✔
408
                }
409
        case fastjson.TypeObject:
×
410
                if i := JSONGetItem(val, prop); i != nil {
×
411
                        _ = it.Append(i)
×
412
                }
×
413
        case fastjson.TypeString:
×
414
                if iri := val.GetStringBytes(); len(iri) > 0 {
×
415
                        _ = it.Append(IRI(iri))
×
416
                }
×
417
        }
418
        if len(it) == 0 {
10✔
419
                return nil
×
420
        }
×
421
        return it
10✔
422
}
423

424
func JSONGetLangRefField(val *fastjson.Value, prop string) LangRef {
×
425
        s := val.Get(prop).GetStringBytes()
×
426
        return MakeRef(s)
×
427
}
×
428

429
func JSONGetIRI(val *fastjson.Value, prop string) IRI {
×
430
        s := val.Get(prop).GetStringBytes()
×
431
        return IRI(s)
×
432
}
×
433

434
// UnmarshalJSON tries to detect the type of the object in the json data and then outputs a matching
435
// ActivityStreams object, if possible
436
func UnmarshalJSON(data []byte) (Item, error) {
14✔
437
        if len(data) == 0 {
14✔
438
                return nil, nil
×
439
        }
×
440
        p := fastjson.Parser{}
14✔
441
        val, err := p.ParseBytes(data)
14✔
442
        if err != nil {
14✔
443
                return nil, err
×
444
        }
×
445
        return JSONUnmarshalToItem(val), nil
14✔
446
}
447

448
func GetItemByType(typ Typer) (Item, error) {
124✔
449
        if typ == nil {
124✔
450
                typ = NilType
×
451
        }
×
452
        switch {
124✔
453
        case ActivityVocabularyTypes{CollectionType}.Match(typ):
2✔
454
                return &Collection{Type: typ}, nil
2✔
455
        case ActivityVocabularyTypes{OrderedCollectionType}.Match(typ):
4✔
456
                return &OrderedCollection{Type: typ}, nil
4✔
457
        case ActivityVocabularyTypes{CollectionPageType}.Match(typ):
2✔
458
                return &CollectionPage{Type: typ}, nil
2✔
459
        case ActivityVocabularyTypes{OrderedCollectionPageType}.Match(typ):
2✔
460
                return &OrderedCollectionPage{Type: typ}, nil
2✔
461
        case ActivityVocabularyTypes{PlaceType}.Match(typ):
2✔
462
                return &Place{Type: typ}, nil
2✔
463
        case ActivityVocabularyTypes{ProfileType}.Match(typ):
2✔
464
                return &Profile{Type: typ}, nil
2✔
465
        case ActivityVocabularyTypes{RelationshipType}.Match(typ):
2✔
466
                return &Relationship{Type: typ}, nil
2✔
467
        case ActivityVocabularyTypes{TombstoneType}.Match(typ):
2✔
468
                return &Tombstone{Type: typ}, nil
2✔
469
        case ActivityVocabularyTypes{QuestionType}.Match(typ):
2✔
470
                return &Question{Type: typ}, nil
2✔
471
        case ActivityVocabularyTypes{ObjectType, ArticleType, AudioType, DocumentType, EventType, ImageType, NoteType, PageType, VideoType}.Match(typ):
20✔
472
                return ObjectNew(typ), nil
20✔
473
        case ActivityVocabularyTypes{LinkType, MentionType}.Match(typ):
4✔
474
                return &Link{Type: typ}, nil
4✔
475
        case ActivityVocabularyTypes{ActivityType, AcceptType, AddType, AnnounceType, BlockType, CreateType, DeleteType, DislikeType,
476
                FlagType, FollowType, IgnoreType, InviteType, JoinType, LeaveType, LikeType, ListenType, MoveType, OfferType,
477
                RejectType, ReadType, RemoveType, TentativeRejectType, TentativeAcceptType, UndoType, UpdateType, ViewType}.Match(typ):
58✔
478
                return &Activity{Type: typ}, nil
58✔
479
        case ActivityVocabularyTypes{IntransitiveActivityType, ArriveType, TravelType}.Match(typ):
6✔
480
                return &IntransitiveActivity{Type: typ}, nil
6✔
481
        case ActivityVocabularyTypes{ActorType, ApplicationType, GroupType, OrganizationType, PersonType, ServiceType}.Match(typ):
12✔
482
                return &Actor{Type: typ}, nil
12✔
483
        case ActivityVocabularyTypes{NilType}.Match(typ):
4✔
484
                fallthrough
4✔
485
        default:
4✔
486
                // when no type is available use a plain Object
4✔
487
                return &Object{}, nil
4✔
488
        }
489
}
490

491
func JSONGetActorEndpoints(val *fastjson.Value, prop string) *Endpoints {
10✔
492
        if val == nil {
10✔
493
                return nil
×
494
        }
×
495
        if val = val.Get(prop); val == nil {
20✔
496
                return nil
10✔
497
        }
10✔
498

499
        e := Endpoints{}
×
500
        e.UploadMedia = JSONGetURIItem(val, "uploadMedia")
×
501
        e.OauthAuthorizationEndpoint = JSONGetURIItem(val, "oauthAuthorizationEndpoint")
×
502
        e.OauthTokenEndpoint = JSONGetURIItem(val, "oauthTokenEndpoint")
×
503
        e.SharedInbox = JSONGetURIItem(val, "sharedInbox")
×
504
        e.ProvideClientKey = JSONGetURIItem(val, "provideClientKey")
×
505
        e.SignClientKey = JSONGetURIItem(val, "signClientKey")
×
506
        e.ProxyURL = JSONGetIRI(val, "proxyUrl")
×
507

×
508
        return &e
×
509
}
510

511
func JSONLoadObject(val *fastjson.Value, o *Object) error {
44✔
512
        o.ID = JSONGetID(val)
44✔
513
        o.Type = JSONGetTypes(val)
44✔
514
        o.Name = JSONGetNaturalLanguageField(val, "name")
44✔
515
        o.Content = JSONGetNaturalLanguageField(val, "content")
44✔
516
        o.Summary = JSONGetNaturalLanguageField(val, "summary")
44✔
517
        o.Context = JSONGetItem(val, "context")
44✔
518
        o.URL = JSONGetURIItem(val, "url")
44✔
519
        o.MediaType = JSONGetMimeType(val, "mediaType")
44✔
520
        o.Generator = JSONGetItem(val, "generator")
44✔
521
        o.AttributedTo = JSONGetItem(val, "attributedTo")
44✔
522
        o.Attachment = JSONGetItem(val, "attachment")
44✔
523
        o.Location = JSONGetItem(val, "location")
44✔
524
        o.Published = JSONGetTime(val, "published")
44✔
525
        o.StartTime = JSONGetTime(val, "startTime")
44✔
526
        o.EndTime = JSONGetTime(val, "endTime")
44✔
527
        o.Duration = JSONGetDuration(val, "duration")
44✔
528
        o.Icon = JSONGetItem(val, "icon")
44✔
529
        o.Preview = JSONGetItem(val, "preview")
44✔
530
        o.Image = JSONGetItem(val, "image")
44✔
531
        o.Updated = JSONGetTime(val, "updated")
44✔
532
        o.InReplyTo = JSONGetItem(val, "inReplyTo")
44✔
533
        o.To = JSONGetItems(val, "to")
44✔
534
        o.Audience = JSONGetItems(val, "audience")
44✔
535
        o.Bto = JSONGetItems(val, "bto")
44✔
536
        o.CC = JSONGetItems(val, "cc")
44✔
537
        o.BCC = JSONGetItems(val, "bcc")
44✔
538
        o.Replies = JSONGetItem(val, "replies")
44✔
539
        o.Tag = JSONGetItems(val, "tag")
44✔
540
        o.Likes = JSONGetItem(val, "likes")
44✔
541
        o.Shares = JSONGetItem(val, "shares")
44✔
542
        o.Source = GetAPSource(val)
44✔
543
        return nil
44✔
544
}
44✔
545

546
func JSONLoadIntransitiveActivity(val *fastjson.Value, i *IntransitiveActivity) error {
16✔
547
        i.Actor = JSONGetItem(val, "actor")
16✔
548
        i.Target = JSONGetItem(val, "target")
16✔
549
        i.Result = JSONGetItem(val, "result")
16✔
550
        i.Origin = JSONGetItem(val, "origin")
16✔
551
        i.Instrument = JSONGetItem(val, "instrument")
16✔
552
        return OnObject(i, func(o *Object) error {
32✔
553
                return JSONLoadObject(val, o)
16✔
554
        })
16✔
555
}
556

557
func JSONLoadActivity(val *fastjson.Value, a *Activity) error {
16✔
558
        a.Object = JSONGetItem(val, "object")
16✔
559
        return OnIntransitiveActivity(a, func(i *IntransitiveActivity) error {
32✔
560
                return JSONLoadIntransitiveActivity(val, i)
16✔
561
        })
16✔
562
}
563

564
func JSONLoadQuestion(val *fastjson.Value, q *Question) error {
×
565
        q.OneOf = JSONGetItem(val, "oneOf")
×
566
        q.AnyOf = JSONGetItem(val, "anyOf")
×
567
        q.Closed = JSONGetBoolean(val, "closed")
×
568
        return OnIntransitiveActivity(q, func(i *IntransitiveActivity) error {
×
569
                return JSONLoadIntransitiveActivity(val, i)
×
570
        })
×
571
}
572

573
func JSONLoadActor(val *fastjson.Value, a *Actor) error {
10✔
574
        a.PreferredUsername = JSONGetNaturalLanguageField(val, "preferredUsername")
10✔
575
        a.Followers = JSONGetItem(val, "followers")
10✔
576
        a.Following = JSONGetItem(val, "following")
10✔
577
        a.Inbox = JSONGetItem(val, "inbox")
10✔
578
        a.Outbox = JSONGetItem(val, "outbox")
10✔
579
        a.Liked = JSONGetItem(val, "liked")
10✔
580
        a.Endpoints = JSONGetActorEndpoints(val, "endpoints")
10✔
581
        a.Streams = JSONGetItems(val, "streams")
10✔
582
        a.PublicKey = JSONGetPublicKey(val, "publicKey")
10✔
583
        return OnObject(a, func(o *Object) error {
20✔
584
                return JSONLoadObject(val, o)
10✔
585
        })
10✔
586
}
587

588
func JSONLoadCollection(val *fastjson.Value, c *Collection) error {
4✔
589
        c.Current = JSONGetItem(val, "current")
4✔
590
        c.First = JSONGetItem(val, "first")
4✔
591
        c.Last = JSONGetItem(val, "last")
4✔
592
        c.TotalItems = uint(JSONGetInt(val, "totalItems"))
4✔
593
        c.Items = JSONGetItems(val, "items")
4✔
594
        return OnObject(c, func(o *Object) error {
8✔
595
                return JSONLoadObject(val, o)
4✔
596
        })
4✔
597
}
598

599
func JSONLoadCollectionPage(val *fastjson.Value, c *CollectionPage) error {
2✔
600
        c.Next = JSONGetItem(val, "next")
2✔
601
        c.Prev = JSONGetItem(val, "prev")
2✔
602
        c.PartOf = JSONGetItem(val, "partOf")
2✔
603
        return OnCollection(c, func(c *Collection) error {
4✔
604
                return JSONLoadCollection(val, c)
2✔
605
        })
2✔
606
}
607

608
func JSONLoadOrderedCollection(val *fastjson.Value, c *OrderedCollection) error {
6✔
609
        c.Current = JSONGetItem(val, "current")
6✔
610
        c.First = JSONGetItem(val, "first")
6✔
611
        c.Last = JSONGetItem(val, "last")
6✔
612
        c.TotalItems = uint(JSONGetInt(val, "totalItems"))
6✔
613
        c.OrderedItems = JSONGetItems(val, "orderedItems")
6✔
614
        return OnObject(c, func(o *Object) error {
12✔
615
                return JSONLoadObject(val, o)
6✔
616
        })
6✔
617
}
618

619
func JSONLoadOrderedCollectionPage(val *fastjson.Value, c *OrderedCollectionPage) error {
2✔
620
        c.Next = JSONGetItem(val, "next")
2✔
621
        c.Prev = JSONGetItem(val, "prev")
2✔
622
        c.PartOf = JSONGetItem(val, "partOf")
2✔
623
        c.StartIndex = uint(JSONGetInt(val, "startIndex"))
2✔
624
        return OnOrderedCollection(c, func(c *OrderedCollection) error {
4✔
625
                return JSONLoadOrderedCollection(val, c)
2✔
626
        })
2✔
627
}
628

629
func JSONLoadPlace(val *fastjson.Value, p *Place) error {
×
630
        p.Accuracy = JSONGetFloat(val, "accuracy")
×
631
        p.Altitude = JSONGetFloat(val, "altitude")
×
632
        p.Latitude = JSONGetFloat(val, "latitude")
×
633
        p.Longitude = JSONGetFloat(val, "longitude")
×
634
        p.Radius = JSONGetInt(val, "radius")
×
635
        p.Units = JSONGetString(val, "units")
×
636
        return OnObject(p, func(o *Object) error {
×
637
                return JSONLoadObject(val, o)
×
638
        })
×
639
}
640

641
func JSONLoadProfile(val *fastjson.Value, p *Profile) error {
×
642
        p.Describes = JSONGetItem(val, "describes")
×
643
        return OnObject(p, func(o *Object) error {
×
644
                return JSONLoadObject(val, o)
×
645
        })
×
646
}
647

648
func JSONLoadRelationship(val *fastjson.Value, r *Relationship) error {
×
649
        r.Subject = JSONGetItem(val, "subject")
×
650
        r.Object = JSONGetItem(val, "object")
×
651
        r.Relationship = JSONGetItem(val, "relationship")
×
652
        return OnObject(r, func(o *Object) error {
×
653
                return JSONLoadObject(val, o)
×
654
        })
×
655
}
656

657
func JSONLoadTombstone(val *fastjson.Value, t *Tombstone) error {
×
658
        t.FormerType = ActivityVocabularyType(JSONGetString(val, "formerType"))
×
659
        t.Deleted = JSONGetTime(val, "deleted")
×
660
        return OnObject(t, func(o *Object) error {
×
661
                return JSONLoadObject(val, o)
×
662
        })
×
663
}
664

665
func jsonLoadToLink(val *fastjson.Value, l *Link) error {
×
666
        l.ID = JSONGetID(val)
×
667
        l.Type = JSONGetTypes(val)
×
668
        l.MediaType = JSONGetMimeType(val, "mediaType")
×
669
        l.Preview = JSONGetItem(val, "preview")
×
670
        if h := JSONGetInt(val, "height"); h != 0 {
×
671
                l.Height = uint(h)
×
672
        }
×
673
        if w := JSONGetInt(val, "width"); w != 0 {
×
674
                l.Width = uint(w)
×
675
        }
×
676
        l.Name = JSONGetNaturalLanguageField(val, "name")
×
677
        if hrefLang := JSONGetLangRefField(val, "hrefLang"); hrefLang.Valid() {
×
678
                l.HrefLang = hrefLang
×
679
        }
×
680
        if href := JSONGetURIItem(val, "href"); href != nil {
×
681
                ll := href.GetLink()
×
682
                if len(ll) > 0 {
×
683
                        l.Href = ll
×
684
                }
×
685
        }
686
        if rel := JSONGetURIItem(val, "rel"); rel != nil {
×
687
                rr := rel.GetLink()
×
688
                if len(rr) > 0 {
×
689
                        l.Rel = rr
×
690
                }
×
691
        }
692
        return nil
×
693
}
694

695
func JSONLoadLink(val *fastjson.Value) (Item, error) {
×
696
        l := new(Link)
×
697
        return l, jsonLoadToLink(val, l)
×
698
}
×
699

700
func JSONLoadPublicKey(val *fastjson.Value, p *PublicKey) error {
×
701
        p.ID = JSONGetID(val)
×
702
        p.Owner = JSONGetIRI(val, "owner")
×
703
        if pub := val.GetStringBytes("publicKeyPem"); len(pub) > 0 {
×
704
                p.PublicKeyPem = string(pub)
×
705
        }
×
706
        return nil
×
707
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc