-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
-
24
-
25
-
26
-
27
-
28
-
29
-
30
-
31
-
32
-
33
-
34
-
35
-
36
-
37
-
38
-
39
-
40
-
41
-
42
-
43
-
44
-
45
-
46
-
47
-
48
-
49
-
50
-
51
-
52
-
53
-
54
-
55
-
56
-
57
-
58
-
59
-
60
-
61
-
62
-
63
-
64
-
65
-
66
-
67
-
68
-
69
-
70
-
71
-
72
-
73
-
74
-
75
-
76
-
77
-
78
-
79
-
80
-
81
-
82
-
83
-
84
-
85
-
86
-
87
-
88
-
89
-
90
-
91
-
92
-
93
-
94
-
95
-
96
-
97
-
98
-
99
-
100
-
101
-
102
-
103
-
104
-
105
-
106
-
107
-
108
-
109
-
110
-
111
-
112
-
113
-
114
-
115
-
116
-
117
-
118
-
119
-
120
-
121
-
122
-
123
-
124
-
125
-
126
-
127
-
128
-
129
-
130
-
131
-
132
-
133
-
134
-
135
-
136
-
137
-
138
-
139
-
140
-
141
-
142
-
143
-
144
-
145
-
146
-
147
-
148
-
149
-
150
-
151
-
152
-
153
-
154
-
155
-
156
-
157
-
158
-
159
-
160
-
161
-
162
package jsonld
import (
"encoding/json"
"fmt"
"reflect"
"strings"
)
const (
tagLabel = "jsonld"
tagOmitEmpty = "omitempty"
tagCollapsible = "collapsible"
)
func Marshal(v interface{}, c *Context) ([]byte, error) {
p := payloadWithContext{c, &v}
return p.MarshalJSON()
}
type payloadWithContext struct {
Context *Context `jsonld:"@context,omitempty,collapsible"`
Obj *interface{}
}
func isEmptyValue(v reflect.Value) bool {
switch v.Kind() {
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
return v.Len() == 0
case reflect.Bool:
return !v.Bool()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return v.Int() == 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return v.Uint() == 0
case reflect.Float32, reflect.Float64:
return v.Float() == 0
case reflect.Interface, reflect.Ptr:
return v.IsNil()
case reflect.Struct:
return func(reflect.Value) bool {
var ret bool = true
for i := 0; i < v.NumField(); i++ {
ret = ret && isEmptyValue(v.Field(i))
}
return ret
}(v)
}
return false
}
func reflectToJsonLdMap(v interface{}) map[string]interface{} {
a := make(map[string]interface{})
typ := reflect.TypeOf(v)
val := reflect.ValueOf(v)
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
jsonLdTag, ok := loadJsonLdTag(cTag)
omitEmpty := ok && jsonLdTag.omitEmpty
if jsonLdTag.ignore {
continue
}
if cField.Anonymous {
for k, v := range reflectToJsonLdMap(cValue.Interface()) {
a[k] = v
}
continue
}
empty := isEmptyValue(cValue)
if !empty || empty && !omitEmpty {
a[jsonLdName(cField.Name, jsonLdTag)] = cValue.Interface()
}
}
return a
}
func (p *payloadWithContext) MarshalJSON() ([]byte, error) {
a := make(map[string]interface{})
if p.Context != nil {
typ := reflect.TypeOf(*p)
cMirror, _ := typ.FieldByName("Context")
jsonLdTag, ok := loadJsonLdTag(cMirror.Tag)
omitEmpty := ok && jsonLdTag.omitEmpty
collapsible := ok && jsonLdTag.collapsible
con := reflectToJsonLdMap(p.Context)
if len(con) > 0 || !omitEmpty {
for _, v := range con {
a[jsonLdName(cMirror.Name, jsonLdTag)] = v
if len(con) == 1 && collapsible {
break
}
}
}
}
if *p.Obj != nil {
oMap := reflectToJsonLdMap(*p.Obj)
if len(oMap) == 0 {
return nil, fmt.Errorf("invalid object to marshall")
}
for k, v := range oMap {
a[k] = v
}
}
return json.Marshal(a)
}
func (p *payloadWithContext) UnmarshalJSON() {}
type Encoder struct{}
type jsonLdTag struct {
name string
ignore bool
omitEmpty bool
collapsible bool
}
func loadJsonLdTag(tag reflect.StructTag) (jsonLdTag, bool) {
jlTag, ok := tag.Lookup(tagLabel)
if !ok {
return jsonLdTag{}, false
}
val := strings.Split(jlTag, ",")
cont := func(arr []string, s string) bool {
for _, v := range arr {
if v == s {
return true
}
}
return false
}
t := jsonLdTag{
omitEmpty: cont(val, tagOmitEmpty),
collapsible: cont(val, tagCollapsible),
}
t.name, t.ignore = func(v string) (string, bool) {
if len(v) > 0 && v != "_" {
return v, false
} else {
return "", true
}
}(val[0])
return t, true
}
func jsonLdName(n string, tag jsonLdTag) string {
if len(tag.name) > 0 {
return tag.name
}
return n
}