Skip to content
Open
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
100 changes: 96 additions & 4 deletions explode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,17 @@ func TestTrue(t *testing.T) {
input := `[true]`
output := `{"0":true}`

out, _ := Explodejsonstr(input, ".")
out, _ := Explodejsonstr(input, ".", -1)
if out != output {
t.Error("got", out)
}
}

func TestTruePassThrough(t *testing.T) {
input := `[true]`
output := input

out, _ := Explodejsonstr(input, ".", 0)
if out != output {
t.Error("got", out)
}
Expand All @@ -18,7 +28,7 @@ func TestNull(t *testing.T) {
input := `[null]`
output := `{"0":null}`

out, _ := Explodejsonstr(input, ".")
out, _ := Explodejsonstr(input, ".", -1)
if out != output {
t.Error("got", out)
}
Expand All @@ -27,7 +37,34 @@ func TestNull(t *testing.T) {
func TestNesting(t *testing.T) {
input := `{"person":{"name":"Joe", "address":{"street":"123 Main St."}}}`
output := `{"person.address.street":"123 Main St.","person.name":"Joe"}`
out, _ := Explodejsonstr(input, ".")
out, _ := Explodejsonstr(input, ".", -1)
if out != output {
t.Error("got", out)
}
}

func TestNestingWithLimit(t *testing.T) {
input := `{"person":{"name":"Joe", "address":{"street":"123 Main St."}}}`
output := `{"person.address":{"street":"123 Main St."},"person.name":"Joe"}`
out, _ := Explodejsonstr(input, ".", 1)
if out != output {
t.Error("got", out)
}
}

func TestNestedPassthrough(t *testing.T) {
input := `{"person":{"name":"Joe", "address":{"street":"123 Main St."}}}`
output := input
out, _ := Explodejsonstr(input, ".", 0)
if out != output {
t.Error("got", out)
}
}

func TestNestingWithSlash(t *testing.T) {
input := `{"person":{"name":"Joe", "address":{"street":"123 Main St."}}}`
output := `{"person/address/street":"123 Main St.","person/name":"Joe"}`
out, _ := Explodejsonstr(input, "/", -1)
if out != output {
t.Error("got", out)
}
Expand Down Expand Up @@ -82,7 +119,62 @@ func TestItems(t *testing.T) {
}
]`
output := `{"0.description":"a schema given for items","0.schema.items.type":"integer","0.tests.0.data.0":1,"0.tests.0.data.1":2,"0.tests.0.data.2":3,"0.tests.0.description":"valid items","0.tests.0.valid":true,"0.tests.1.data.0":1,"0.tests.1.data.1":"x","0.tests.1.description":"wrong type of items","0.tests.1.valid":false,"0.tests.2.data.foo":"bar","0.tests.2.description":"ignores non-arrays","0.tests.2.valid":true,"1.description":"an array of schemas for items","1.schema.items.0.type":"integer","1.schema.items.1.type":"string","1.tests.0.data.0":1,"1.tests.0.data.1":"foo","1.tests.0.description":"correct types","1.tests.0.valid":true,"1.tests.1.data.0":"foo","1.tests.1.data.1":1,"1.tests.1.description":"wrong types","1.tests.1.valid":false}`
out, _ := Explodejsonstr(input, ".")
out, _ := Explodejsonstr(input, ".", -1)
if out != output {
t.Error("got", out)
}
}

func TestItemsWithLimit(t *testing.T) {
input := `
[
{
"description": "a schema given for items",
"schema": {
"items": {"type": "integer"}
},
"tests": [
{
"description": "valid items",
"data": [ 1, 2, 3 ],
"valid": true
},
{
"description": "wrong type of items",
"data": [1, "x"],
"valid": false
},
{
"description": "ignores non-arrays",
"data": {"foo" : "bar"},
"valid": true
}
]
},
{
"description": "an array of schemas for items",
"schema": {
"items": [
{"type": "integer"},
{"type": "string"}
]
},
"tests": [
{
"description": "correct types",
"data": [ 1, "foo" ],
"valid": true
},
{
"description": "wrong types",
"data": [ "foo", 1 ],
"valid": false
}
]
}
]`
output := `{"0.description":"a schema given for items","0.schema.items":{"type":"integer"},"0.tests.0":{"data":[1,2,3],"description":"valid items","valid":true},"0.tests.1":{"data":[1,"x"],"description":"wrong type of items","valid":false},"0.tests.2":{"data":{"foo":"bar"},"description":"ignores non-arrays","valid":true},"1.description":"an array of schemas for items","1.schema.items":[{"type":"integer"},{"type":"string"}],"1.tests.0":{"data":[1,"foo"],"description":"correct types","valid":true},"1.tests.1":{"data":["foo",1],"description":"wrong types","valid":false}}`
out, _ := Explodejsonstr(input, ".", 2)
if out != output {
t.Error("got", out)
}
Expand Down
48 changes: 37 additions & 11 deletions gojsonexplode.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"strconv"
)

func explodeList(l []interface{}, parent string, delimiter string) (map[string]interface{}, error) {
func explodeList(l []interface{}, parent string, delimiter string, depth int) (map[string]interface{}, error) {
var err error
var key string
j := make(map[string]interface{})
Expand All @@ -18,6 +18,12 @@ func explodeList(l []interface{}, parent string, delimiter string) (map[string]i
} else {
key = strconv.Itoa(k)
}

if depth == 0 {
j[key] = i
continue
}

switch v := i.(type) {
case nil:
j[key] = v
Expand All @@ -31,7 +37,7 @@ func explodeList(l []interface{}, parent string, delimiter string) (map[string]i
j[key] = v
case []interface{}:
out := make(map[string]interface{})
out, err = explodeList(v, key, delimiter)
out, err = explodeList(v, key, delimiter, (depth - 1))
if err != nil {
return nil, err
}
Expand All @@ -40,7 +46,7 @@ func explodeList(l []interface{}, parent string, delimiter string) (map[string]i
}
case map[string]interface{}:
out := make(map[string]interface{})
out, err = explodeMap(v, key, delimiter)
out, err = explodeMap(v, key, delimiter, (depth - 1))
if err != nil {
return nil, err
}
Expand All @@ -54,13 +60,20 @@ func explodeList(l []interface{}, parent string, delimiter string) (map[string]i
return j, nil
}

func explodeMap(m map[string]interface{}, parent string, delimiter string) (map[string]interface{}, error) {
func explodeMap(m map[string]interface{}, parent string, delimiter string, depth int) (map[string]interface{}, error) {
var err error

j := make(map[string]interface{})
for k, i := range m {
if len(parent) > 0 {
k = parent + delimiter + k
}

if depth == 0 {
j[k] = i
continue
}

switch v := i.(type) {
case nil:
j[k] = v
Expand All @@ -74,7 +87,7 @@ func explodeMap(m map[string]interface{}, parent string, delimiter string) (map[
j[k] = v
case []interface{}:
out := make(map[string]interface{})
out, err = explodeList(v, k, delimiter)
out, err = explodeList(v, k, delimiter, (depth - 1))
if err != nil {
return nil, err
}
Expand All @@ -83,7 +96,7 @@ func explodeMap(m map[string]interface{}, parent string, delimiter string) (map[
}
case map[string]interface{}:
out := make(map[string]interface{})
out, err = explodeMap(v, k, delimiter)
out, err = explodeMap(v, k, delimiter, (depth - 1))
if err != nil {
return nil, err
}
Expand All @@ -99,7 +112,7 @@ func explodeMap(m map[string]interface{}, parent string, delimiter string) (map[

// Explodejson takes in a nested JSON as a byte array and a delimiter and returns an
// exploded/flattened json byte array
func Explodejson(b []byte, d string) ([]byte, error) {
func Explodejson(b []byte, d string, depth int) ([]byte, error) {
var input interface{}
var exploded map[string]interface{}
var out []byte
Expand All @@ -110,12 +123,12 @@ func Explodejson(b []byte, d string) ([]byte, error) {
}
switch t := input.(type) {
case map[string]interface{}:
exploded, err = explodeMap(t, "", d)
exploded, err = explodeMap(t, "", d, depth)
if err != nil {
return nil, err
}
case []interface{}:
exploded, err = explodeList(t, "", d)
exploded, err = explodeList(t, "", d, depth)
if err != nil {
return nil, err
}
Expand All @@ -135,12 +148,25 @@ func Explodejson(b []byte, d string) ([]byte, error) {
// parameters to pass to the function are
// * s: the JSON string
// * d: the delimiter to use when unnesting the JSON object.
// * depth : the desired depth of nesting
// -1 = no depth limit
// 0 = no nesting ( returns same as input )
// 1 = only first parent will be nested, e.g. company.address
// 2 = up until second child, e.g. company.adress.street
// 3 = ...
// Set to -1 if depth limit is not desired
//
// {"person":{"name":"Joe", "address":{"street":"123 Main St."}}}
// explodes to:
// {"person.name":"Joe", "person.address.street":"123 Main St."}
func Explodejsonstr(s string, d string) (string, error) {
func Explodejsonstr(s string, d string, depth int) (string, error) {

if depth == 0 {
return s, nil
}

b := []byte(s)
out, err := Explodejson(b, d)
out, err := Explodejson(b, d, depth)
if err != nil {
return "", err
}
Expand Down