Git Product home page Git Product logo

gabs's Introduction

Gabs

pkg.go for Jeffail/gabs

Gabs is a small utility for dealing with dynamic or unknown JSON structures in Go. It's pretty much just a helpful wrapper for navigating hierarchies of map[string]interface{} objects provided by the encoding/json package. It does nothing spectacular apart from being fabulous.

If you're migrating from version 1 check out migration.md for guidance.

Use

Import

Using modules:

import (
	"github.com/Jeffail/gabs/v2"
)

Without modules:

import (
	"github.com/Jeffail/gabs"
)

Parsing and searching JSON

jsonParsed, err := gabs.ParseJSON([]byte(`{
	"outer":{
		"inner":{
			"value1":10,
			"value2":22
		},
		"alsoInner":{
			"value1":20,
			"array1":[
				30, 40
			]
		}
	}
}`))
if err != nil {
	panic(err)
}

var value float64
var ok bool

value, ok = jsonParsed.Path("outer.inner.value1").Data().(float64)
// value == 10.0, ok == true

value, ok = jsonParsed.Search("outer", "inner", "value1").Data().(float64)
// value == 10.0, ok == true

value, ok = jsonParsed.Search("outer", "alsoInner", "array1", "1").Data().(float64)
// value == 40.0, ok == true

gObj, err := jsonParsed.JSONPointer("/outer/alsoInner/array1/1")
if err != nil {
	panic(err)
}
value, ok = gObj.Data().(float64)
// value == 40.0, ok == true

value, ok = jsonParsed.Path("does.not.exist").Data().(float64)
// value == 0.0, ok == false

exists := jsonParsed.Exists("outer", "inner", "value1")
// exists == true

exists = jsonParsed.ExistsP("does.not.exist")
// exists == false

Iterating objects

jsonParsed, err := gabs.ParseJSON([]byte(`{"object":{"first":1,"second":2,"third":3}}`))
if err != nil {
	panic(err)
}

// S is shorthand for Search
for key, child := range jsonParsed.S("object").ChildrenMap() {
	fmt.Printf("key: %v, value: %v\n", key, child.Data().(float64))
}

Iterating arrays

jsonParsed, err := gabs.ParseJSON([]byte(`{"array":["first","second","third"]}`))
if err != nil {
	panic(err)
}

for _, child := range jsonParsed.S("array").Children() {
	fmt.Println(child.Data().(string))
}

Will print:

first
second
third

Children() will return all children of an array in order. This also works on objects, however, the children will be returned in a random order.

Searching through arrays

If your structure contains arrays you must target an index in your search.

jsonParsed, err := gabs.ParseJSON([]byte(`{"array":[{"value":1},{"value":2},{"value":3}]}`))
if err != nil {
	panic(err)
}
fmt.Println(jsonParsed.Path("array.1.value").String())

Will print 2.

Generating JSON

jsonObj := gabs.New()
// or gabs.Wrap(jsonObject) to work on an existing map[string]interface{}

jsonObj.Set(10, "outer", "inner", "value")
jsonObj.SetP(20, "outer.inner.value2")
jsonObj.Set(30, "outer", "inner2", "value3")

fmt.Println(jsonObj.String())

Will print:

{"outer":{"inner":{"value":10,"value2":20},"inner2":{"value3":30}}}

To pretty-print:

fmt.Println(jsonObj.StringIndent("", "  "))

Will print:

{
  "outer": {
    "inner": {
      "value": 10,
      "value2": 20
    },
    "inner2": {
      "value3": 30
    }
  }
}

Generating Arrays

jsonObj := gabs.New()

jsonObj.Array("foo", "array")
// Or .ArrayP("foo.array")

jsonObj.ArrayAppend(10, "foo", "array")
jsonObj.ArrayAppend(20, "foo", "array")
jsonObj.ArrayAppend(30, "foo", "array")

fmt.Println(jsonObj.String())

Will print:

{"foo":{"array":[10,20,30]}}

Working with arrays by index:

jsonObj := gabs.New()

// Create an array with the length of 3
jsonObj.ArrayOfSize(3, "foo")

jsonObj.S("foo").SetIndex("test1", 0)
jsonObj.S("foo").SetIndex("test2", 1)

// Create an embedded array with the length of 3
jsonObj.S("foo").ArrayOfSizeI(3, 2)

jsonObj.S("foo").Index(2).SetIndex(1, 0)
jsonObj.S("foo").Index(2).SetIndex(2, 1)
jsonObj.S("foo").Index(2).SetIndex(3, 2)

fmt.Println(jsonObj.String())

Will print:

{"foo":["test1","test2",[1,2,3]]}

Converting back to JSON

This is the easiest part:

jsonParsedObj, _ := gabs.ParseJSON([]byte(`{
	"outer":{
		"values":{
			"first":10,
			"second":11
		}
	},
	"outer2":"hello world"
}`))

jsonOutput := jsonParsedObj.String()
// Becomes `{"outer":{"values":{"first":10,"second":11}},"outer2":"hello world"}`

And to serialize a specific segment is as simple as:

jsonParsedObj := gabs.ParseJSON([]byte(`{
	"outer":{
		"values":{
			"first":10,
			"second":11
		}
	},
	"outer2":"hello world"
}`))

jsonOutput := jsonParsedObj.Search("outer").String()
// Becomes `{"values":{"first":10,"second":11}}`

Merge two containers

You can merge a JSON structure into an existing one, where collisions will be converted into a JSON array.

jsonParsed1, _ := ParseJSON([]byte(`{"outer":{"value1":"one"}}`))
jsonParsed2, _ := ParseJSON([]byte(`{"outer":{"inner":{"value3":"three"}},"outer2":{"value2":"two"}}`))

jsonParsed1.Merge(jsonParsed2)
// Becomes `{"outer":{"inner":{"value3":"three"},"value1":"one"},"outer2":{"value2":"two"}}`

Arrays are merged:

jsonParsed1, _ := ParseJSON([]byte(`{"array":["one"]}`))
jsonParsed2, _ := ParseJSON([]byte(`{"array":["two"]}`))

jsonParsed1.Merge(jsonParsed2)
// Becomes `{"array":["one", "two"]}`

Parsing Numbers

Gabs uses the json package under the bonnet, which by default will parse all number values into float64. If you need to parse Int values then you should use a json.Decoder:

sample := []byte(`{"test":{"int":10,"float":6.66}}`)
dec := json.NewDecoder(bytes.NewReader(sample))
dec.UseNumber()

val, err := gabs.ParseJSONDecoder(dec)
if err != nil {
    t.Errorf("Failed to parse: %v", err)
    return
}

intValue, err := val.Path("test.int").Data().(json.Number).Int64()

gabs's People

Contributors

cliffano avatar dsapala avatar flowchartsman avatar gruetter avatar jeffail avatar jinleileiking avatar katcipis avatar mihaitodor avatar moorereason avatar nibbleshift avatar nwest1 avatar p-apoorva avatar sammyrock avatar spgb avatar tobieiss avatar tylerstillwater avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

gabs's Issues

Generating arrays with recursion

Currently, I didn't find a way to generate an array with this case of recursion:
I have a structure which is parsed and then being interpreted in a function as a value to set in a container.

Consider the following code

type DataObject struct {
	Value  *string
	Values []*DataObject
}

func toJSON(data *DataObject, rootName string, currContaier, root *gabs.Container) {
	if data.Values != nil {
		newContainer, _ := currContaier.Array(rootName)
		for i, val := range data.Values {
			toJSON(val, "", newContainer, root)
		}
	} else { // data.Value != nil, always true here
		currContaier.ArrayAppend(data.Value, "") // WRONG, obviously
	}
}

Is there any way to do this without having the function itself being aware of the index in the array I'm trying to build?

Doesn't work with int.

For this json:
{ "login":{ "service":99, "id":"12345" }, "me":{ "name": "ABC DEF" } }

id_service, ok_1 := reqJson.Path("login.service").Data().(int)

ok_1 returns false.
If I change 99 to be a string, and change the type assertion it works.

Json structure - equivalent gabs commands

Supposed I have the given JSON structure:

{
   "name" : "external",
   "children" : [
      {
         "size" : 1,
         "name" : "WikiLeaks"
      },
      {
         "name" : "PanamaLeaks",
         "size" : 1
      },
      {
         "name" : "internal",
         "children" : [
            {
               "children" : [
                  {
                     "size" : 1,
                     "name" : "ZPR"
                  },
                  {
                     "size" : 1,
                     "name" : "ZMR"
                  }
               ],
               "name" : "bmi"
            },
            {
               "name" : "bmeia",
               "children" : [
                  {
                     "name" : "AWO",
                     "size" : 1
                  },
                  {
                     "size" : 1,
                     "name" : "AWD"
                  }
               ]
            },
            {
               "name" : "bmf",
               "children" : [
                  {
                     "children" : [
                        {
                           "name" : "GU",
                           "size" : 1
                        },
                        {
                           "name" : "FGH",
                           "size" : 1
                        }
                     ],
                     "name" : "core"
                  }
               ]
            }
         ]
      }
   ]
}

How do the corresponding Gabs-Commands look like?

How can I create a complex JSON

How can I create a complex JSON, like this:
{"body":
{"content":
[ {"a":"1","b":"2"},
{"c":"3","d","4"}
]
}
}

I try,but has no idea

unable to get go get github.com/Jeffail/gabs

github.com/Jeffail/gabs

../go/src/github.com/Jeffail/gabs/gabs.go:429: e.SetEscapeHTML undefined (type *json.Encoder has no field or method SetEscapeHTML)
../go/src/github.com/Jeffail/gabs/gabs.go:436: e.SetIndent undefined (type *json.Encoder has no field or method SetIndent)
../go/src/github.com/Jeffail/gabs/gabs.go:447: encoder.SetEscapeHTML undefined (type *json.Encoder has no field or method SetEscapeHTML)

Why does String() return a value wrapped in quotes?

I've been hitting this recently and finding it really weird!

I would expect String() to return a string containing the value, but it seems to include the containing quotes. For example, I would expect this to output "Matches":

package main

import (
    "fmt"

    "github.com/jeffail/gabs"
)

func main() {
    jsonParsed, _ := gabs.ParseJSON([]byte(`{
    "test":{
      "key": "value"
    }
  }`))

    if jsonParsed.Path("test.key").String() == "value" {
        fmt.Println("Matches")
    } else {
        fmt.Println("Doesn't match")
    }
}

On the other hand this works fine, but String() should work from a new user to the library point of view:

if jsonParsed.Path("test.key").Data() == "value" {
    fmt.Println("Matches")
} else {
    fmt.Println("Doesn't match")
}

question

do you have anymore performance tests?
I am looking at using this in a opensource OAI project. Just want to compare to other packages.

Add an 'Exists' method to return a bool representing whether or not a key exists

I have a use case in which the existence of a particular attribute determines how I interact with the parsed JSON.

While the functionality is technically there:

// fields
_, exists = jsonParsed.Search("myProperty").Data().(float64)
if exists {

}
// objects
_, err = jsonParsed.Search("myObjProperty").ChildrenMap()
if err == nil {

}
// arrays
_, err = jsonParsed.Search("myArrProperty").Children()
if err == nil {

}

I think it would be convenient to have a simple and unified interface for doing so:

if jsonParsed.Exists("myPath", "to", "myProperty") {

} 

question about nested nested nested JSON

Hello ! First, thank you for your work :)
I do have a question concerning how to use gabs for nested JSON and I'd like your help.
I have this complicated JSON (from Tumblr API) link_to_json

And I'd like for each post in response.posts, to get the ["tags"] and to get the url from "original_size" from photos.

I actually began to write :

posts_parsed, err := gabs.ParseJSON( getContents(posts_json_url) )
            if err != nil {
                log.Fatal("Error parsing blog JSON !")
                return "json success=false, error="
            }
            posts, _ := jsonParsed.S("response.posts").Children()
            for _, post_ar := range posts {
                post, _ := jsonParsed.S(post_ar).ChildrenMap()
                if post["type"] != "photo" {
                    continue
                }
                for _,tag_name := range post["tags"] {
                    tags := strings.Split(tag_name, " ")
                    for _,tag_title := range tags {
                    ............

but I don't know if it's correct.
Would it be possible for you to add a complex JSON iteration in your doc ?
Thank you for your help :D

Check existence

This may be related to: #14

In your example:

value, ok = jsonParsed.Search("outter", "inner", "value1").Data().(float64)
// value == 10.0, ok == true

value, ok = jsonParsed.Path("does.not.exist").Data().(float64)
// value == 0.0, ok == false

The second example is actually ambiguous.
ok could be false either because the path does not exist OR because the data could not be type asserted to float64.

Is there another way to just determine if the path exists -- irrespective of the data type or whether the type assertion failed.

Is there anyway to get the json object keys

Greetings,
I would like to extract the keys from below json object. Is there any way using to it this library?

{
  "id": 790,
  "sid": "[email protected]",
  "dstHost": "55.23.220.21",
  "srcHost": "58.226.2.241",
  "dstId": "55.23.220.21:5060",
  "srcId": "58.226.2.241:5064",
  "srcIp": "58.226.2.241",
  "dstIp": "55.23.220.21",
  "srcPort": 5064,
  "aliasSrc": "58.226.2.241:5064",
  "aliasDst": "55.23.220.21:5060",
  "dstPort": 5060,
  "method": "INVITE",
  "method_text": "INVITE",
  "create_date": "2019-09-04T14:44:10.7896334+05:00",
  "protocol": 17,
  "msg_color": "blue",
  "ruri_user": "INVITE sip:[email protected];user=phone SIP/2.0\r\n",
  "destination": 1,
  "micro_ts": "0001-01-01T00:00:00Z"
}

Best Regards,

Append second json object at a specific location in first json object

Greetings,
I have two json objects.
First one.

{
  "hosts": {
    "58.226.2.241:5064": {
      "host": [
        "58.226.2.241:5064"
      ],
      "position": "1"
    }
  }
}

Second

{
  "127.0.0.1:5064": {
    "host": [
      "127.0.0.1:5064"
    ]
  },
  "position": "2"
}

I would like to append second to first after the "hosts" key so the result should look like below.

{
  "hosts": {
    "58.226.2.241:5064": {
      "host": [
        "58.226.2.241:5064"
      ],
      "position": "1"
    },
    "127.0.0.1:5060": {
      "host": [
        "127.0.0.1:5060"
      ],
      "position": "2"
    }
  }
}

But i am miserably failing here. https://play.golang.org/p/daB-y98d2F-

Thanks.

Delete an element from an array

Hi!
Nice work for this lib' ๐Ÿ‘

I'm dealing to remove an element from an array.
I used Delete, but this method deals only with objects.

Is a method exists for my problem, because I don't see how to make this happen easily...?

Thanks a lot!

Doesn't work with object in array

jsonObj := gabs.New()
	jsonObj.Set("Test1", "Tag")
	jsonObj.ArrayP("Fields")
	jsonObj1 := gabs.New()
	jsonObj1.Set("Test2", "Tag")
	jsonObj.ArrayAppend(jsonObj1, "Fields")
	fmt.Println(jsonObj.String())

result:
{"Fields":[{}],"Tag":"Test1"}

expecting:
{"Fields":[{"Tag":"Test2}],"Tag":"Test1"}

Deleting from a json

Hi Jeffail,
Firstly, thank you for writing this library. It saved my teammates and I a lot of trouble with dealing with maps. I was wondering if there are any plans of supporting deletes (e.g. delete a field withing a container given a path).

something wrong with ints

I am sending this in an request to my golang app:

{"id":2027907052821425841}

Goapp app:
_id, ok_1 := reqJson.Path("id").Data().(float64)
id := uint64(_id)

I am adding id to my database and it ends up being:2027907052821425920.

What is going on here?

It should be 2027907052821425841 so now my database is inconsistent. The database field is set up to unsigned bigint which ranges from [0, 18446744073709551615]

Merge that allow to replace instead of generating an array.

Here a small change I would like to submit for review:

Adding a replace bool in the Merge function to allow to replace an object instead of creating an array.

// Merge - Merges two gabs-containers
func (g *Container) Merge(toMerge *Container, replace bool) error {
var recursiveFnc func(map[string]interface{}, []string) error
recursiveFnc = func(mmap map[string]interface{}, path []string) error {
for key, value := range mmap {
newPath := append(path, key)
if !replace && g.Exists(newPath...) {
target := g.Search(newPath...)
switch t := value.(type) {
case map[string]interface{}:
switch targetV := target.Data().(type) {
case map[string]interface{}:
if err := recursiveFnc(t, newPath); err != nil {
return err
}
case []interface{}:
g.Set(append(targetV, t), newPath...)
default:
newSlice := append([]interface{}{}, targetV)
g.Set(append(newSlice, t), newPath...)
}
case []interface{}:
for _, valueOfSlice := range t {
if err := g.ArrayAppend(valueOfSlice, newPath...); err != nil {
return err
}
}
default:
switch targetV := target.Data().(type) {
case []interface{}:
g.Set(append(targetV, t), newPath...)
default:
newSlice := append([]interface{}{}, targetV)
g.Set(append(newSlice, t), newPath...)
}
}
} else {
// path doesn't exist. So set the value
if _, err := g.Set(value, newPath...); err != nil {
return err
}
}
}
return nil
}
if mmap, ok := toMerge.Data().(map[string]interface{}); ok {
return recursiveFnc(mmap, []string{})
}
return nil
}

Get methods

Would you accept a pull request with some useful Get methods for specific types ala jason?

The point is that gabs seems to be good for building up data in Containers and jason seems to be good at reading values. But it is useful to have both (in one package). So I would propose adding basically the same methods Get* that jason has.

Comments?

Outer array?

Can you provide examples of how to access values when top level json is an array?

i.e.

[{"hello":"friend"},{"good":"morning"}]

Maybe it'd be nice to be able to get a slice of jsonParsed? Or Be able to search via index like "0.hello"?

Generate array in array

Hi @Jeffail,

Thank you very much for this nice package.

Is there an easier way to achieve following json:

{
  "columns": [
    [
      "a",
      "1"
    ],
    [
      "a",
      "2"
    ],
    [
      "b",
      "1"
    ],
    [
      "b",
      "2"
    ]
  ]
}

Is there an easier way than this "dummy" workaround?

package main

import (
    "fmt"
    "github.com/jeffail/gabs"
)

func main() {

    letters := []string{"a", "b"}
    numbers := []string{"1", "2"}

    outerArray := gabs.New()
    outerArray.Array("columns")

    for _, letter := range letters {
        for _, number := range numbers {

            innerArray := gabs.New()
            innerArray.Array("dummy")
            innerArray.ArrayAppend(letter, "dummy")
            innerArray.ArrayAppend(number, "dummy")

            outerArray.ArrayAppend(innerArray.Path("dummy").Data(), "columns")
        }
    }
    fmt.Println(outerArray.StringIndent("", "  "))
}

Thanks

ArrayAppend add null

newArray = append(newArray, g.Search(path...).Data())
g.Search(path...).Data() get null

i change
if v:= g.Search(path...).Data() ; v != nil{
newArray = append(newArray,v)
}

Support json path with indices

Hi,

It would be great to have support for indices in json path's when setting a value.

E.g.
jsonObj := gabs.New() jsonObj.Set(10, "outter", "inner", "0", "value") fmt.Println(jsonObj.StringIndent("", " "))

Expected result:
{ "outter": { "inner": { "0": { "value": 10 } } } }

Proposal as patch attached
0001-Setter-index-support-added.txt

Best regards
Giovanni

RFC: Gabs Version 2

I'm planning a version 2 release of gabs with a few quality of life improvements. The best summary of the proposed changes is in the migration.md document I made: https://github.com/Jeffail/gabs/blob/wip/v2/migration.md.

The change is primarily two things:

  1. Explicit indexes required when querying arrays, described here
  2. Removing error return parameters for methods that don't require it

The justification for 2. is that a lot of functions that currently return errors (Consume, Children, etc) would only do so when the argument cannot be cast to the expected type (usually a map[string]interface{}.)

Gabs is primarily useful in situations where these explicit checks aren't required, and if they are required you are always able to check the underlying type explicitly, so I feel this is a positive change.

However, I'm opening up for comments, questions, angry demands, etc. If you have strong thoughts about any of these changes or wish to propose more changes for version 2 please do so here. I'll probably keep this issue open for a week.

generating json

@Jeffail thanks for this package, very easy to use. just having problem after getting all keys i need how to generate new json from sample below. could you maybe provide some snippet if possible

{
    "name": "sample",
    "def": [{
        "setId": 1,
        "setDef": [{
            "name": "ABC",
            "type": "STRING",
            "property": {
                "field1": "text1",
                "field2": "text2"
            }
        }, {
            "name": "XYZ",
            "type": "STRING",
            "property": {
                "field1": "text1",
                "field2": "text2"
            }
        }]
    }, {
        "setId": 2,
        "setDef": [{
            "name": "abc",
            "type": "STRING",
            "property": {
                "field1": "text1",
                "field2": "text2"
            }
        }, {
            "name": "xyz",
            "type": "STRING",
            "property": {
                "field1": "text1",
                "field2": "text2"
            }
        }]
    }]
}

Reading array within an array in Response Body

Consider the following response body:

{
    "result": [
        [
            {
                "data": 21.1734,
                "dateTime": "2018-05-22T14:44:00.000Z",
                "device": {
                    "id": "abcd"
                },
                "diagnostic": {
                    "id": "Diagnostic1"
                },
                "id": null
            }
        ],
        [
            {
                "data": 50.973,
                "dateTime": "2018-05-22T14:44:00.000Z",
                "device": {
                    "id": "abc"
                },
                "diagnostic": {
                    "id": "Diagnostic2"
                },
                "id": null
            }
        ]
    ],
    "jsonrpc": "2.0"
}

I wish to get the value of "data" field. I am stuck at this:

result, _  := jsonParsed.S("result").Children()
for _, dataArray := rangeResult {
   // What do I do here? 
}

restructure a json object

Greetings,
I have a JSON object like below.

[
  {
    "create_date": "2019-08-28T07:01:20.000332-04:00",
    "data_header": {
      "callid": "[email protected]"
    },
    "id": 761,
    "protocol_header": {
      "captureId": "2001"
    },
    "sid": "[email protected]"
  },
  {
    "create_date": "2019-08-28T07:01:30.000581-04:00",
    "data_header": {
      "callid": "[email protected]"
    },
    "id": 762,
    "protocol_header": {
      "captureId": "2002"
    },
    "sid": "[email protected]"
  }
]

I would like to restructure it like below.

[
  {
    "callid": "[email protected]",
    "captureId": "2001",
    "create_date": "2019-08-28T07:01:20.000332-04:00",
    "id": 761,
    "sid": "[email protected]"
  },
  {
    "callid": "[email protected]",
    "captureId": "2002",
    "create_date": "2019-08-28T07:01:30.000581-04:00",
    "id": 762,
    "sid": "[email protected]"
  }
]

I have tried so far this https://play.golang.org/p/g_9W08zigif but my method is adding an empty object into array.

Any suggestion how can I handle this better?

Best Regards,

Gabs will not parse an array at the top level

From (http://www.json.org):

JSON is built on two structures:

A collection of name/value pairs. In various languages, this is realized as an object, record, struct, dictionary, hash table, keyed list, or associative array.
An ordered list of values. In most languages, this is realized as an array, vector, list, or sequence.

When passing an array into gabs:

import (
    "github.com/jeffail/gabs"
    "fmt"
)

json, err := gabs.ParseJSON([]byte(`[]`))
fmt.Printf("%+v", err)

The input text could not be parsed error is generated. It seems to be because of this line in func ParseJSON:

if _, ok := gabs.object.(map[string]interface{}); ok {

Whether or not that if statement is the only thing preventing parsing arrays, I'm not sure of but I think this is something that definitely needs resolving.

Feature Request: Work with streams

In go's http responses, the Body is provided as an io.ReadCloser data type. It would be great if it were possible to pass that directly into gabs without loading the entire json into memory.

I took an initial look at the code and couldn't see anything helpful so I'll do my best to see if I can provide a PR even with my little go experience.

Merge that only adds unique values

Hi @Jeffail
great library!!!
For the Merge function I was searching for a way to only moving values into an array when they are not the same:
Merging:

{
"funny": 4
}

And

{
"funny": 4
}

Should not result in

{
"funny": [4, 4]
}

I created a little proof of concept on my fork

s00500@c65dca3

What do you think? do you have any ideas on how to do it more elegant? Otherwise I can submit a PR and update documentation

Greetings,
Lukas

runtime error: invalid memory address or nil pointer dereference

I am not able to work with gabs. It always throws this panic error. That's my code:

response, err := http.Get(uri)
if err != nil {
    return nil, nil, err
}
defer response.Body.Close()
contents, err := ioutil.ReadAll(response.Body)
fmt.Println(string(contents))
jsonParsed, err := gabs.ParseJSON(contents)

........

How to write into http.ResponseWriter?

Hi, your library so amazing, but i have difficulty to write into http.ResponseWriter, because data always empty when i try on Postman

Below is my code
jsonObj := gabs.New()
// or gabs.Consume(jsonObject) to work on an existing map[string]interface{}
jsonObj.Set(10, "categories", "category", "label")
jsonObj.Set(10, "dataset", "seriesname")
jsonObj.Set(20, "dataset","data","value")
jsonObj.Set("Y", "feedback")
b, err := json.MarshalIndent(jsonObj, "", " ")
if err != nil {
panic(err)
}
w.Write(b)

Empty values in nested JSON

I have a very strange bug and am not sure if it is me or what is going wrong.

Take this JSON (I ran it through a validator, looks OK):

{
  "VCALENDAR": {
    "CALSCALE": "GREGORIAN",
    "PRODID": "-//Product.//0.1.0//EN",
    "VERSION": "2.0",
    "VTIMEZONE": {
      "TZID": "Europe/Paris",
      "DAYLIGHT": {
        "DTSTART": "19810329T020000",
        "RRULE": "FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU",
        "TZNAME": "GMT+2",
        "TZOFFSETFROM": "+0100",
        "TZOFFSETTO": "+0200"
      },
      "STANDARD": {
        "DTSTART": "19961027T030000",
        "RRULE": "FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU",
        "TZNAME": "GMT+1",
        "TZOFFSETFROM": "+0200",
        "TZOFFSETTO": "+0100"
      }
    },
    "VEVENT": {
      "CREATED": "20180816T190181Z",
      "LAST-MODIFIED": "20180816T190181Z",
      "DTSTAMP": "20180816T190181Z",
      "DTSTART": "20180816T200000Z",
      "DTEND": "20180816T210000Z",
      "SEQUENCE": "0",
      "SUMMARY": "Demo Event 1534446101813",
      "TRANSP": "OPAQUE",
      "UID": "e972e6c8-77a3-4ef8-9bd1-42ff69593489"
    }
  }
}

When I run that JSON through your gabs.ParseJSON function, I get no error whatsoever, some of the structure, but not all (only one level deep), and I only get the values on the top level.

I get VCALENDAR.VERSION and value of it. I get VCALENDAR.VTIMEZONE.DAYLIGHT, but no value and no sub-level. I get VCALENDAR.VEVENT.CREATED, but not value.

Is there some kind of restriction of depth or something?

how to merge two arrays

Greetings,
i have two json objects like below.
First One.

[
  {
    "data_header": {
      "callid": "[email protected]_b2b-1"
    },
    "id": 676,
    "protocol_header": {
      "captureId": "2001"
    }
  }
]

Second One.

[
  {
    "id": 54,
    "protocol_header": {
      "captureId": "2002"
    },
    "data_header": {
      "callid": "[email protected]",
      "node": "2001"
    }
  }
]

I would like to merge them so they like below.

[
  {
    "id": 54,
    "protocol_header": {
      "captureId": "2002"
    },
    "data_header": {
      "callid": "[email protected]",
      "node": "2001"
    }
  },
  {
    "data_header": {
      "callid": "[email protected]_b2b-1"
    },
    "id": 676,
    "protocol_header": {
      "captureId": "2001"
    }
  }
]

Any hint how can I achieve the above result?
Best Regards,

floating point numbers

Hi @Jeffail,

me again :) I have the problem that json.Unmarshal creates floating point numbers from long numbers. I use following function when decoding static json to prevent this problem:

//JSONDecode decodes json long numbers not as floating point numbers
func JSONDecode(response string) (map[string]interface{}, error) {

    /**
     * Instead of json.Unmarshal 
     * use this:
     * http://stackoverflow.com/questions/22343083/json-marshaling-with-long-numbers-in-golang-gives-floating-point-number
     * json.Unmarshal converts long numbers to 1.0e+07
     * UseNumber() does the trick here
     */

    var result map[string]interface{}
    d := json.NewDecoder(strings.NewReader(response))
    d.UseNumber()
    if err := d.Decode(&result); err != nil {
        Trace(err)
        return nil, err
    }
    return result, nil
}

Any chance to integrate this into gabs?

Nested Container

I have the functions A & B that generate different parameters as JSON. I give this container to a function C, which creates a new container and should use the result from A/B as parameter. This appears to be currently not possible (setting a container creates an empty key-pair).

Example code

func finalize(method string, params *gabs.Container) (*gabs.Container, error) {
    reqMap := gabs.New()
    reqMap.Set("val", "name")
    if params != nil {
        fmt.Println("Append: ", params.String())
        reqMap.Set(params, "params")
    }
    fmt.Println(reqMap.String());
}

Another use case is for example if i want to generate an array out of objects. I could then just append the objects via ArrayAppend.

parse Int64 value

invalid type assertion: jsonParsed.Path("job_id").Data().(json.Number).(int64) (non-interface type json.Number on left)

null value being added to container

Hey there, love being able to use this in my work. :) I'm encountering some behavior I didn't expect, sample code is below.

package main

import (
	"fmt"

	"github.com/Jeffail/gabs"
)

func verifyItem(item interface{}, jsonObj *gabs.Container) bool {
	_, isString := item.(string)
	if isString {
		return true
	}
	jsonObj.ArrayAppend(item, "notString")
	return false
}

func main() {
	jsonParsed, _ := gabs.ParseJSON([]byte(`{"outer": [1,2,3,"four","five","six"]}`))
	// jsonParsed, _ := gabs.ParseJSON([]byte(`{"outer": ["four","five","six"]}`))
	children, _ := jsonParsed.Search("outer").Children()
	jsonObj := gabs.New()
	for _, child := range children {
		verifyItem(child.Data(), jsonObj)
	}
	fmt.Println(jsonObj.String())
}

I'd expect that the result would be {"notString":[1,2,3]}, but for some reason, null is being added in: {"notString":[null,1,2,3]}. In this example, I'm hoping to only create the notString key if any interfaces are identified as not being a string. That works (toggling jsonParsed gives {} when only passing strings), but whenever a non-string is detected, null gets added. Any ideas?

SetP to update arrays

Hi,

Does the SetP method allow to update an array?
Something like:

c.SetP(1, "prop.array.$1")

If yes, what is the syntax for a path to an array index?

Thank you

fatal error: concurrent map read and map write

I sometimes run into this error, are you going to implement RWMutex by any chance?

fatal error: concurrent map read and map write

goroutine 6686 [running]:
runtime.throw(0xb5f048, 0x21)
	/usr/lib/go-1.8/src/runtime/panic.go:596 +0x95 fp=0xc423619408 sp=0xc4236193e8
runtime.mapaccess2_faststr(0xa854e0, 0xc423d02510, 0xb4eab4, 0x9, 0xc424c805f0, 0xc423619578)
	/usr/lib/go-1.8/src/runtime/hashmap_fast.go:326 +0x50a fp=0xc423619468 sp=0xc423619408
github.com/Jeffail/gabs.(*Container).Search(0xc42446de40, 0xc4236196f0, 0x1, 0x1, 0xf)
	/home/mda/.local/go/src/github.com/Jeffail/gabs/gabs.go:98 +0xb1 fp=0xc423619550 sp=0xc423619468
github.com/Jeffail/gabs.(*Container).Exists(0xc42446de40, 0xc4236196f0, 0x1, 0x1, 0xc424c805f0)
	/home/mda/.local/go/src/github.com/Jeffail/gabs/gabs.go:129 +0x49 fp=0xc423619588 sp=0xc423619550
... 

Create JSON from CSV

I have a CSV like

id,name,size
external,Facebook,25
external.internal,MyApp,17

and I would like to create a JSON dynamically like
{
"id":"external",
"children": [{"name": "Facebook"}, {"size":25},
{"id": "internal,
"children": [{"name":"MyApp"}, {"size":17}]
}
]
}

I though this should be trivial with gabs. I already collect the CSV headers. What I have so far is

{
				if idx == 0 {
					jc, _ = jc.ObjectP(str)
					// The first element defines the hierarchy
					jc, err = jc.SetP(str, "id")
					if idx := strings.LastIndex(str, "."); idx > 0 {
						path = str[0 : idx+1]
					}
					path += "children"
				} else {
					if jc, err = jc.ArrayP(path); err != nil {
						log.Fatalf("ArrayP: at index %d, path %s, value: %s - %s", idx, path, str, err.Error())
					}

					if jc = jc.ArrayAppendP(str, path+"."+heading[idx]); err != nil {
						log.Fatalf("SetP: at index %d, path %s, value: %s - %s", idx, path, str, err.Error())
					}
				}
			}

But all it creates when calling jc.StringIdent is a single element (when there are no values besides id in the CSV or it fails telling me that there is a collision.

any way to sort the json array based upon time

I have a JSON array like below and would like to sort this based upon the time feild.

[
  {
    "create_date": "2019-08-28T07:01:10.000382-04:00",
    "data_header": {
      "callid": "[email protected]"
    }
  },
  {
    "create_date": "2019-08-28T07:01:30.000631-04:00",
    "data_header": {
      "callid": "[email protected]",
      
    }
  },
  {
    "create_date": "2019-08-28T07:01:20.000631-04:00",
    "data_header": {
      "callid": "[email protected]",
      
    }
  }
]

So, the result should like this below.

[
  {
    "create_date": "2019-08-28T07:01:10.000382-04:00",
    "data_header": {
      "callid": "[email protected]"
    }
  },
  {
    "create_date": "2019-08-28T07:01:20.000631-04:00",
    "data_header": {
      "callid": "[email protected]",
      
    }
  },
  {
    "create_date": "2019-08-28T07:01:30.000631-04:00",
    "data_header": {
      "callid": "[email protected]",
      
    }
  }
]

Any suggestion would be much appreciated. Thanks.

Delete Array

The current Delete function is limited to basic json formats. If the JSON contains an array, we cannot delete the first item in the array or delete a key in the first element of a json array.

keys get lost

Hi @Jeffail,

I have another question. Have a look at following program:

package main

import (
    "fmt"

    "github.com/jeffail/gabs"
)

func main() {

    json, _ := gabs.ParseJSON([]byte(`{
        "data" : {
            "x1" : {
                "a" : 1,
                "b" : 2,
                "c" : 3
            },
            "x2" : {
                "a" : 4,
                "b" : 5,
                "c" : 6
            }
        }
    }`))
    x, _ := json.Path("data").Children()
    for xKey, xValue := range x {
        fmt.Println(xKey, " - ", xValue)
    }
}

The response is:

0  -  {"a":1,"b":2,"c":3}
1  -  {"a":4,"b":5,"c":6}

You see, the keys "x1" and "x2" get lost.

Now I use following workaround

x, _ = json.Path("data").Data().(map[string]interface{})

to get the keys and iterate through them. But then I have to use following within the range:

a := json.Path("data."+xKey+".a").Data().(float64)
b := json.Path("data."+xKey+".b").Data().(float64)
c := json.Path("data."+xKey+".c").Data().(float64)

This works but could be nicer if Children() would return the keys if using with a json object. Or do you have another solution for this?

Thank you very much

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.