Added basic unmarshalling for objects with jsonld context

This commit is contained in:
Marius Orcsik 2018-06-07 15:32:14 +02:00
parent 1997a54824
commit 175fd5fdeb
No known key found for this signature in database
GPG key ID: 889CE8E4FB2D877A
4 changed files with 216 additions and 17 deletions

View file

@ -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
}

View file

@ -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 {

View file

@ -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

View file

@ -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)
}
}