Added basic unmarshalling for objects with jsonld context
This commit is contained in:
parent
1997a54824
commit
175fd5fdeb
4 changed files with 216 additions and 17 deletions
|
@ -2,8 +2,10 @@ package jsonld
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"activitypub"
|
||||
ap "activitypub"
|
||||
)
|
||||
|
||||
// Ref basic type
|
||||
|
@ -13,8 +15,8 @@ type Ref string
|
|||
// Terms are case sensitive and any valid string that is not a reserved JSON-LD
|
||||
// keyword can be used as a term.
|
||||
type Context struct {
|
||||
URL Ref `jsonld:"@url"`
|
||||
Language activitypub.NaturalLanguageValue `jsonld:"@language,omitempty,collapsible"`
|
||||
URL Ref `jsonld:"@url"`
|
||||
Language ap.LangRef `jsonld:"@language,omitempty,collapsible"`
|
||||
}
|
||||
|
||||
// Collapsible is an interface used by the JSON-LD marshaller to collapse a struct to one single value
|
||||
|
@ -45,3 +47,46 @@ func (c *Context) MarshalJSON() ([]byte, error) {
|
|||
}
|
||||
return json.Marshal(a.object)
|
||||
}
|
||||
|
||||
// UnmarshalJSON tries to load the Context from the incoming json value
|
||||
func (c *Context) UnmarshalJSON(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 Context value")
|
||||
}
|
||||
c.URL = Ref(data[1 : len(data)-1])
|
||||
}
|
||||
if data[0] == '{' {
|
||||
// an object - trying to load it to the struct
|
||||
if data[len(data)-1] != '}' {
|
||||
return fmt.Errorf("invalid object value when unmarshalling Context value")
|
||||
}
|
||||
|
||||
var s scanner
|
||||
s.reset()
|
||||
var d decodeState
|
||||
d.scan = s
|
||||
d.init(data)
|
||||
|
||||
d.scan.reset()
|
||||
|
||||
t := d.valueInterface()
|
||||
|
||||
for key, value := range t.(map[string]interface{}) {
|
||||
switch strings.ToLower(key) {
|
||||
case "@url":
|
||||
fallthrough
|
||||
case "url":
|
||||
c.URL = Ref(value.(string))
|
||||
case "@language":
|
||||
fallthrough
|
||||
case "language":
|
||||
c.Language = ap.LangRef(value.(string))
|
||||
default:
|
||||
return fmt.Errorf("unkown Context field %q", key)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package jsonld
|
||||
|
||||
import (
|
||||
"activitypub"
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
|
@ -32,8 +31,7 @@ func TestContext_Ref(t *testing.T) {
|
|||
func TestContext_MarshalJSON(t *testing.T) {
|
||||
url := "test"
|
||||
c := Context{URL: Ref(url)}
|
||||
c.Language = make(activitypub.NaturalLanguageValue, 1)
|
||||
c.Language["en"] = "en-GB"
|
||||
c.Language = "en-GB"
|
||||
|
||||
out, err := c.MarshalJSON()
|
||||
if err != nil {
|
||||
|
|
|
@ -14,10 +14,6 @@ import (
|
|||
"unicode/utf8"
|
||||
)
|
||||
|
||||
func (p *payloadWithContext) UnmarshalJSON([]byte) error {
|
||||
return fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
// Unmarshal parses the JSON-encoded data and stores the result
|
||||
// in the value pointed to by v. If v is nil or not a pointer,
|
||||
// Unmarshal returns an InvalidUnmarshalError.
|
||||
|
@ -91,17 +87,15 @@ func (p *payloadWithContext) UnmarshalJSON([]byte) error {
|
|||
// character U+FFFD.
|
||||
//
|
||||
func Unmarshal(data []byte, v interface{}) error {
|
||||
// Check for well-formedness.
|
||||
// Avoids filling out half a data structure
|
||||
// before discovering a JSON syntax error.
|
||||
var d decodeState
|
||||
|
||||
err := checkValid(data, &d.scan)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.init(data)
|
||||
return d.unmarshal(v)
|
||||
return d.unmarshal(&v)
|
||||
}
|
||||
|
||||
// Unmarshaler is the interface implemented by types
|
||||
|
|
|
@ -1,7 +1,169 @@
|
|||
package jsonld
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
func TestPayloadWithContext_UnmarshalJSON(t *testing.T) {
|
||||
t.Skipf("Not implemented")
|
||||
ap "activitypub"
|
||||
)
|
||||
|
||||
func TestUnmarshalWithEmptyJsonObject(t *testing.T) {
|
||||
obj := mockTypeA{}
|
||||
err := Unmarshal([]byte("{}"), &obj)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if obj.Id != "" {
|
||||
t.Errorf("Id should have been an empty string, found %s", obj.Id)
|
||||
}
|
||||
if obj.Name != "" {
|
||||
t.Errorf("Name should have been an empty string, found %s", obj.Name)
|
||||
}
|
||||
if obj.Type != "" {
|
||||
t.Errorf("Type should have been an empty string, found %s", obj.Type)
|
||||
}
|
||||
if obj.PropA != "" {
|
||||
t.Errorf("PropA should have been an empty string, found %s", obj.PropA)
|
||||
}
|
||||
if obj.PropB != 0 {
|
||||
t.Errorf("PropB should have been 0.0, found %f", obj.PropB)
|
||||
}
|
||||
}
|
||||
|
||||
type mockWithContext struct {
|
||||
mockTypeA
|
||||
Context Context `jsonld:"@context"`
|
||||
}
|
||||
|
||||
func TestUnmarshalWithEmptyJsonObjectWithStringContext(t *testing.T) {
|
||||
obj := mockWithContext{}
|
||||
url := "http://www.habarnam.ro"
|
||||
data := []byte(`{"@context": "` + url + `" }`)
|
||||
err := Unmarshal(data, &obj)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if obj.Context.Ref() != Ref(url) {
|
||||
t.Errorf("@context should have been %q, found %q", url, obj.Context.Collapse())
|
||||
}
|
||||
if obj.Id != "" {
|
||||
t.Errorf("Id should have been an empty string, found %s", obj.Id)
|
||||
}
|
||||
if obj.Name != "" {
|
||||
t.Errorf("Name should have been an empty string, found %s", obj.Name)
|
||||
}
|
||||
if obj.Type != "" {
|
||||
t.Errorf("Type should have been an empty string, found %s", obj.Type)
|
||||
}
|
||||
if obj.PropA != "" {
|
||||
t.Errorf("PropA should have been an empty string, found %s", obj.PropA)
|
||||
}
|
||||
if obj.PropB != 0 {
|
||||
t.Errorf("PropB should have been 0.0, found %f", obj.PropB)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalWithEmptyJsonObjectWithObjectContext(t *testing.T) {
|
||||
obj := mockWithContext{}
|
||||
url := "http://www.habarnam.ro"
|
||||
data := []byte(`{"@context": { "@url": "` + url + `"} }`)
|
||||
err := Unmarshal(data, &obj)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if obj.Context.Ref() != Ref(url) {
|
||||
t.Errorf("@context should have been %q, found %q", url, obj.Context.Collapse())
|
||||
}
|
||||
if obj.Id != "" {
|
||||
t.Errorf("Id should have been an empty string, found %s", obj.Id)
|
||||
}
|
||||
if obj.Name != "" {
|
||||
t.Errorf("Name should have been an empty string, found %s", obj.Name)
|
||||
}
|
||||
if obj.Type != "" {
|
||||
t.Errorf("Type should have been an empty string, found %s", obj.Type)
|
||||
}
|
||||
if obj.PropA != "" {
|
||||
t.Errorf("PropA should have been an empty string, found %s", obj.PropA)
|
||||
}
|
||||
if obj.PropB != 0 {
|
||||
t.Errorf("PropB should have been 0.0, found %f", obj.PropB)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalWithEmptyJsonObjectWithOneLanguageContext(t *testing.T) {
|
||||
obj := mockWithContext{}
|
||||
url := "http://www.habarnam.ro"
|
||||
langEn := "en-US"
|
||||
|
||||
data := []byte(`{"@context": { "@url": "` + url + `", "@language": "` + langEn + `"} }`)
|
||||
err := Unmarshal(data, &obj)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if obj.Context.Ref() != Ref(url) {
|
||||
t.Errorf("@context should have been %q, found %q", url, obj.Context.Collapse())
|
||||
}
|
||||
if obj.Context.Language != ap.LangRef(langEn) {
|
||||
t.Errorf("@context should have been %q, found %q", url, obj.Context.Collapse())
|
||||
}
|
||||
if obj.Id != "" {
|
||||
t.Errorf("Id should have been an empty string, found %s", obj.Id)
|
||||
}
|
||||
if obj.Name != "" {
|
||||
t.Errorf("Name should have been an empty string, found %s", obj.Name)
|
||||
}
|
||||
if obj.Type != "" {
|
||||
t.Errorf("Type should have been an empty string, found %s", obj.Type)
|
||||
}
|
||||
if obj.PropA != "" {
|
||||
t.Errorf("PropA should have been an empty string, found %s", obj.PropA)
|
||||
}
|
||||
if obj.PropB != 0 {
|
||||
t.Errorf("PropB should have been 0.0, found %f", obj.PropB)
|
||||
}
|
||||
}
|
||||
func TestUnmarshalWithEmptyJsonObjectWithFullObject(t *testing.T) {
|
||||
obj := mockWithContext{}
|
||||
url := "http://www.habarnam.ro"
|
||||
langEn := "en-US"
|
||||
propA := "ana"
|
||||
var propB float32 = 6.66
|
||||
typ := "test"
|
||||
name := "test object #1"
|
||||
id := "777sdad"
|
||||
|
||||
data := []byte(`{
|
||||
"@context": { "@url": "` + url + `", "@language": "` + langEn + `"},
|
||||
"PropA": "` + propA + `",
|
||||
"PropB": ` + strconv.FormatFloat(float64(propB), 'f', 2, 32) + `,
|
||||
"Id" : "` + id + `",
|
||||
"Name" : "` + name + `",
|
||||
"Type" : "` + typ + `"
|
||||
}`)
|
||||
err := Unmarshal(data, &obj)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if obj.Context.Ref() != Ref(url) {
|
||||
t.Errorf("@context should have been %q, found %q", url, obj.Context.Collapse())
|
||||
}
|
||||
if obj.Context.Language != ap.LangRef(langEn) {
|
||||
t.Errorf("@context should have been %q, found %q", url, obj.Context.Collapse())
|
||||
}
|
||||
if obj.Id != id {
|
||||
t.Errorf("Id should have been %q, found %q", id, obj.Id)
|
||||
}
|
||||
if obj.Name != name {
|
||||
t.Errorf("Name should have been %q, found %q", name, obj.Name)
|
||||
}
|
||||
if obj.Type != typ {
|
||||
t.Errorf("Type should have been %q, found %q", typ, obj.Type)
|
||||
}
|
||||
if obj.PropA != propA {
|
||||
t.Errorf("PropA should have been %q, found %q", propA, obj.PropA)
|
||||
}
|
||||
if obj.PropB != propB {
|
||||
t.Errorf("PropB should have been %f, found %f", propB, obj.PropB)
|
||||
}
|
||||
}
|
||||
|
|
Reference in a new issue