Added a method for marshalling interface{}s which decay to scalars

for the moment it's overcomplicted and ugly

Moved UnmarshalJSON to decode.go file

Adding tests for some of the new functionality
This commit is contained in:
Marius Orcsik 2017-10-02 17:24:26 +02:00
parent b2d595e31d
commit 9d9bd8510b
No known key found for this signature in database
GPG key ID: C36D1EBE93A6EEAE
4 changed files with 100 additions and 30 deletions

View file

@ -20,8 +20,12 @@ func (c *Context) Ref() Ref {
func (r *Ref) MarshalText() ([]byte, error) { func (r *Ref) MarshalText() ([]byte, error) {
return []byte(*r), nil return []byte(*r), nil
} }
func (c *Context) MarshalJSON() ([]byte, error) { func (c *Context) MarshalJSON() ([]byte, error) {
var a map[string]interface{} a := reflectToJsonValue(c)
a = reflectToJsonLdMap(c) if a.isScalar {
return json.Marshal(a) return json.Marshal(a.scalar)
} else {
return json.Marshal(a.object)
}
} }

View file

@ -1,10 +1,10 @@
package jsonld package jsonld
import ( import (
"activitypub"
"bytes" "bytes"
"strings" "strings"
"testing" "testing"
"activitypub"
) )
func TestRef_MarshalText(t *testing.T) { func TestRef_MarshalText(t *testing.T) {

View file

@ -49,14 +49,46 @@ func isEmptyValue(v reflect.Value) bool {
return false return false
} }
func reflectToJsonLdMap(v interface{}) map[string]interface{} { type jsonCapableValue struct {
a := make(map[string]interface{}) isScalar bool
scalar interface{}
object map[string]interface{}
}
func reflectToJsonValue(v interface{}) jsonCapableValue {
a := jsonCapableValue{}
a.object = make(map[string]interface{})
typ := reflect.TypeOf(v) typ := reflect.TypeOf(v)
val := reflect.ValueOf(v) val := reflect.ValueOf(v)
if typ.Kind() == reflect.Ptr { if typ.Kind() == reflect.Ptr {
typ = typ.Elem() typ = typ.Elem()
val = val.Elem() val = val.Elem()
} }
switch typ.Kind() {
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
a.scalar = val.Interface()
a.isScalar = true
return a
case reflect.Bool:
a.scalar = val.Bool()
a.isScalar = true
return a
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
a.scalar = val.Int()
a.isScalar = true
return a
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
a.scalar = val.Uint()
a.isScalar = true
return a
case reflect.Float32, reflect.Float64:
a.scalar = val.Float()
a.isScalar = true
return a
case reflect.Interface, reflect.Ptr:
a.isScalar = true
return a
}
for i := 0; i < typ.NumField(); i++ { for i := 0; i < typ.NumField(); i++ {
cField := typ.Field(i) cField := typ.Field(i)
@ -69,14 +101,17 @@ func reflectToJsonLdMap(v interface{}) map[string]interface{} {
continue continue
} }
if cField.Anonymous { if cField.Anonymous {
for k, v := range reflectToJsonLdMap(cValue.Interface()) { anonJsonVal := reflectToJsonValue(cValue.Interface())
a[k] = v if anonJsonVal.isScalar {
}
continue continue
} }
for k, v := range anonJsonVal.object {
a.object[k] = v
}
}
empty := isEmptyValue(cValue) empty := isEmptyValue(cValue)
if !empty || empty && !omitEmpty { if !empty || empty && !omitEmpty {
a[jsonLdName(cField.Name, jsonLdTag)] = cValue.Interface() a.object[jsonLdName(cField.Name, jsonLdTag)] = cValue.Interface()
} }
} }
@ -84,38 +119,50 @@ func reflectToJsonLdMap(v interface{}) map[string]interface{} {
} }
func (p *payloadWithContext) MarshalJSON() ([]byte, error) { func (p *payloadWithContext) MarshalJSON() ([]byte, error) {
a := make(map[string]interface{}) a := jsonCapableValue{}
a.isScalar = true
a.object = make(map[string]interface{})
if p.Context != nil { if p.Context != nil {
a.isScalar = false
typ := reflect.TypeOf(*p) typ := reflect.TypeOf(*p)
cMirror, _ := typ.FieldByName("Context") cMirror, _ := typ.FieldByName("Context")
jsonLdTag, ok := loadJsonLdTag(cMirror.Tag) jsonLdTag, ok := loadJsonLdTag(cMirror.Tag)
omitEmpty := ok && jsonLdTag.omitEmpty omitEmpty := ok && jsonLdTag.omitEmpty
collapsible := ok && jsonLdTag.collapsible collapsible := ok && jsonLdTag.collapsible
con := reflectToJsonLdMap(p.Context) con := reflectToJsonValue(p.Context)
if len(con) > 0 || !omitEmpty { if len(con.object) > 0 || !omitEmpty {
for _, v := range con { for _, v := range con.object {
a[jsonLdName(cMirror.Name, jsonLdTag)] = v a.object[jsonLdName(cMirror.Name, jsonLdTag)] = v
if len(con) == 1 && collapsible { if len(con.object) == 1 && collapsible {
break break
} }
} }
} }
} }
if *p.Obj != nil { if p.Obj != nil {
oMap := reflectToJsonLdMap(*p.Obj) oMap := reflectToJsonValue(*p.Obj)
if len(oMap) == 0 { if oMap.isScalar && a.isScalar {
a.isScalar = true
a.scalar = oMap.scalar
} else {
if len(oMap.object) == 0 {
return nil, fmt.Errorf("invalid object to marshall") return nil, fmt.Errorf("invalid object to marshall")
} else {
a.isScalar = false
} }
for k, v := range oMap { for k, v := range oMap.object {
a[k] = v a.object[k] = v
} }
} }
return json.Marshal(a) }
if a.isScalar {
return json.Marshal(a.scalar)
} else {
return json.Marshal(a.object)
}
} }
func (p *payloadWithContext) UnmarshalJSON() {}
type Encoder struct{} type Encoder struct{}
type jsonLdTag struct { type jsonLdTag struct {

View file

@ -118,8 +118,27 @@ func TestIsEmpty(t *testing.T) {
} }
func TestPayloadWithContext_MarshalJSON(t *testing.T) { func TestPayloadWithContext_MarshalJSON(t *testing.T) {
t.Skipf("Not implemented") empty := payloadWithContext{}
} eData, eErr := empty.MarshalJSON()
func TestPayloadWithContext_UnmarshalJSON(t *testing.T) {
t.Skipf("Not implemented") if eErr != nil {
t.Errorf("Error: %s", eErr)
}
n, _ := json.Marshal(nil)
if bytes.Compare(eData, n) != 0 {
t.Errorf("Empty payload should resolve to null json value '%s', received '%s'", n, eData)
}
var a interface{}
a = 1
p := payloadWithContext{Obj: &a}
pData, pErr := p.MarshalJSON()
if pErr != nil {
t.Errorf("Error: %s", pErr)
}
av, _ := json.Marshal(a)
if bytes.Compare(pData, av) != 0 {
t.Errorf("Empty payload should resolve to value '%#v', received '%s'", av, pData)
}
} }