Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Recursive Parse #6

Merged
merged 1 commit into from
Apr 12, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@ module github.com/aimof/jason

go 1.12

require golang.org/x/arch v0.0.0-20190312162104-788fe5ffcd8c // indirect
require (
golang.org/x/arch v0.0.0-20190312162104-788fe5ffcd8c // indirect
golang.org/x/tools v0.0.0-20190411180116-681f9ce8ac52 // indirect
)
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
golang.org/x/arch v0.0.0-20190312162104-788fe5ffcd8c h1:Rx/HTKi09myZ25t1SOlDHmHOy/mKxNAcu0hP1oPX9qM=
golang.org/x/arch v0.0.0-20190312162104-788fe5ffcd8c/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190411180116-681f9ce8ac52 h1:9RlW/mHPSeoxtqVWkJ7ZugoTFX8WFZRzmCep/niCbtU=
golang.org/x/tools v0.0.0-20190411180116-681f9ce8ac52/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
80 changes: 76 additions & 4 deletions jason.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ func (k KeyNotFoundError) Error() string {
// It may contain a bool, number, string, object, array or null.
type Value struct {
data interface{}
exists bool // Used to separate nil and non-existing values
exists bool // Used to separate nil and non-existing values
err error // True when the value is invalid.
}

// Object represents an object JSON object.
Expand All @@ -93,6 +94,11 @@ type Object struct {
valid bool
}

type Array struct {
Value
s []*Value
}

// Marshal into bytes.
func (v *Object) MarshalJSON() ([]byte, error) {
return json.Marshal(v.m)
Expand All @@ -104,6 +110,72 @@ func (v *Object) Map() map[string]*Value {
return v.m
}

func NewValue(reader io.Reader) (*Value, error) {
v, err := newValueFromReader(reader)
if err != nil {
return nil, err
}

return v, nil
}

func dataToObject(src *Value) (dist *Value) {
switch src.data.(type) {
case map[string]interface{}:
dist := new(Object)
for key, v := range src.data.(map[string]interface{}) {
if v == nil {
dist.m[key] = &Value{data: nil, exists: false}
}
switch v.(type) {
case map[string]interface{}:
dist.data.(map[string]interface{})[key] = &Value{data: dataToObject(&Value{data: v})}
default:
dist.data = &Value{data: v}
}
}
case []interface{}:
dist := make([]*Value, len(src.data.([]interface{})))
for i, v := range src.data.([]interface{}) {
dist[i].data = &Value{data: dataToObject(&Value{data: v})}
}
}
return dist
}

func (parent *Value) Get(i interface{}) *Value {
if parent == nil {
return &Value{
err: fmt.Errorf("Get %v: parent is nil", i),
}
}
if parent.err != nil {
return &*parent
}
switch i.(type) {
case string:
switch parent.Interface().(type) {
case map[string]interface{}:
child, ok := parent.Interface().(map[string]interface{})[i.(string)]
if !ok {
return &Value{err: fmt.Errorf("Get: %v is not in keys", i)}
}
return &Value{data: child}
default:
parent.err = fmt.Errorf("Get %v: parent is not an object", i)
}
case int:
switch parent.Interface().(type) {
case []interface{}:
if i.(int) < len(parent.Interface().([]interface{})) {
return &Value{data: parent.Interface().([]interface{})[i.(int)]}
}
return &Value{err: fmt.Errorf("Get %v: Index out of range", i)}
}
}
return &Value{err: fmt.Errorf("Get: %v is invalid", i)}
}

// Creates a new value from an io.reader.
// Returns an error if the reader does not contain valid json.
// Useful for parsing the body of a net/http response.
Expand Down Expand Up @@ -656,7 +728,7 @@ func (v *Value) Array() ([]*Value, error) {
if valid {

for _, element := range v.data.([]interface{}) {
child := Value{element, true}
child := Value{data: element, exists: true}
slice = append(slice, &child)
}

Expand Down Expand Up @@ -760,7 +832,7 @@ func (v *Value) Object() (*Object, error) {

if valid {
for key, element := range v.data.(map[string]interface{}) {
m[key] = &Value{element, true}
m[key] = &Value{data: element, exists: true}

}
}
Expand Down Expand Up @@ -795,7 +867,7 @@ func (v *Value) ObjectArray() ([]*Object, error) {
if valid {

for _, element := range v.data.([]interface{}) {
childValue := Value{element, true}
childValue := Value{data: element, exists: true}
childObject, err := childValue.Object()

if err != nil {
Expand Down
139 changes: 139 additions & 0 deletions jason_highly_nested_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package jason

import (
"encoding/json"
"reflect"
"strings"
"testing"
)

var j = `[
{
"a": [
{
"3": 5
},
{
"2": 3
},
{
"b": [
{
"n": 2
},
{
"b": "c"
},
{
"c": [
{
"d": 1
},
{
"u": 2
}
]
}
]
}
]
},
{
"a": [
{
"3": 5
},
{
"2": 3
},
{
"b": [
{
"n": 2
},
{
"b": "c"
},
{
"c": [
{
"d": 1
},
{
"f": [
[
2,
3,
4,
{
"tik": 1
}
],
3,
5
]
}
]
}
]
}
]
},
{
"a": [
{
"3": 5
},
{
"2": 3
},
{
"b": [
{
"n": 2
},
{
"b": "c"
},
{
"c": [
{
"d": 1
},
{
"i": 2
}
]
}
]
}
]
}
]`

func TestHighlyNestedJason(t *testing.T) {
values, err := NewValueFromReader(strings.NewReader(j))
if err != nil {
t.Error(err)
return
}
v := values.Get(1).Get("a").Get(2).Get("b").Get(2).Get("c").Get(1).Get("f").Get(0).Get(3).Get("tik")
if v == nil {
t.Error()
return
}
if v.err != nil {
t.Error(v.err)
return
}
switch v.Interface().(type) {
case json.Number:
if num, err := v.Interface().(json.Number).Int64(); err != nil {
t.Error()
} else if num != 1 {
t.Error()
}
default:
t.Error(reflect.TypeOf(v.Interface()))
}
}
55 changes: 55 additions & 0 deletions jason_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package jason

import (
"bytes"
"encoding/json"
"log"
"reflect"
"strings"
"testing"
)

Expand All @@ -12,6 +14,59 @@ import (
// So, new test doesn't use assert.
// ---

func TestNewValueRecursiveFromReader(t *testing.T) {
j = `{
"Foo": {
"Bar": {
"Fizz":[
"Buzz"
]
}
},
"Bar": [
{
"Foo": "Bar"
},
3,
"Fizz"
]
}`

values, err := NewValue(strings.NewReader(j))
if err != nil {
t.Error(err)
return
}
v := values.Get("Foo").Get("Bar").Get("Fizz").Get(0)
if v == nil {
t.Error()
return
}
if v.err != nil {
t.Error(v.err)
return
}
switch v.Interface().(type) {
case string:
if v.Interface().(string) != "Buzz" {
t.Error(v)
}
default:
t.Error(reflect.TypeOf(v.Interface()))
}
v = values.Get("Bar").Get(1)
switch v.Interface().(type) {
case json.Number:
if num, err := v.Interface().(json.Number).Int64(); err != nil {
t.Error()
} else if num != 3 {
t.Error()
}
default:
t.Error(reflect.TypeOf(v.Interface()))
}
}

func TestNewValueWithObject(t *testing.T) {
jsonValues := make([]*Value, 0, 2)

Expand Down