Accessing fields in a GO structure - go

I am new to the Golang world and trying to parse a json response returned by AWS API.
I've tried parsing the result by dot notation and seem to get success in the higher levels. Below is what my experimentation resulted to.
For brevity I excluded other fields
Test 1
fmt.Println(result)
returns
{
DBClusterSnapshots: [{
Status: "available"
}]
}
Test 2
fmt.Println(result.DBClusterSnapshots[0])
returns
{
Status: "available"
}
Test 3
fmt.Println(result.DBClusterSnapshots[0].Status)
returns what seem to be a reference to an object
0xc0001e74c8
Given the last example (Test 3) how do I parse it properly to get the value of Status which is "available"

As pointed out by #mkopriva
Status is a pointer and therefore need to be dereference when intending to extract the string.
so to achieve extracting the value of status we can dereference it like this.
s := *result.DBClusterSnapshots[0].Status
fmt.Println(s)

Related

Json response in Go(Gin) without escape characters

I have recently started working on Go APIs using GIN. My API is getting the data from DB with two columns where one column contains integer and other contains a json string.
The json string is dynamic and hence i cant use struct for that.
I am using map[string]interface{} to parse the json and modify it and then parse it back to json using json.Marshal. Now i am returning this json string as a response but getting escape characters. Have done some search regarding that, but didnt find any solution yet.
Here is the part of code that i am using
var interface_obj map[string]interface{}
json.Unmarshal([]byte(grants.Data), &interface_obj)
grants_map := interface_obj["role_grants"].(map[string]interface{})
jsonString, err := json.Marshal(grants_map)
jsonBody := string(jsonString)
After this, I am returning this JSON as response in GIN framework like this
c.JSON(http.StatusCreated, gin.H{"message": "Json retrieved successfully", "data": jsonBody})
But the output i am getting is
{
"data": "[{\"action\":\"read\",\"resource\":\"project\"},{\"action\":\"all\",\"resource\":\"users\"},{\"action\":\"all\",\"resource\":\"roles\"},{\"action\":\"all\",\"resource\":\"project-settings\"},{\"action\":\"create\",\"resource\":\"single-entity-screening\"},{\"action\":\"read\",\"resource\":\"single-entity-screening\"},{\"action\":\"create\",\"resource\":\"multi-batch-screening\"},{\"action\":\"read\",\"resource\":\"multi-batch-screening\"},{\"action\":\"read\",\"resource\":\"workspace\"},{\"action\":\"allocate\",\"resource\":\"workspace\"},{\"action\":\"update\",\"resource\":\"workspace\"},{\"action\":\"read\",\"resource\":\"case\"},{\"action\":\"allocate\",\"resource\":\"case\"},{\"action\":\"review\",\"resource\":\"case\"},{\"action\":\"update\",\"resource\":\"case\"},{\"action\":\"read\",\"resource\":\"report\"},{\"action\":\"read\",\"resource\":\"audit-trail\"},{\"action\":\"read\",\"resource\":\"delivery\"}]",
"message": "Grants retrieved successfully"
}
I printed it on my console and it looked fine there, but causing this issue on response.
Is there any way to resolve this using some standard way? Please guide
Thanks
You don't need to do json.Marshal(grants_map), just pass the value directly to gin.H and let c.JSON do the encoding, i.e.
gin.H{... "data": grants_map}
And in cases where you truly have raw JSON data at hand that you want to send as part of other not-yet-JSON data, you can wrap it into json.RawMessage to avoid the "double-encoding", i.e.
gin.H{... "data": json.RawMessage(jsonBody)}

Powerautomate Parsing JSON Array

I've seen the JSON array questions here and I'm still a little lost, so could use some extra help.
Here's the setup:
My Flow calls a sproc on my DB and that sproc returns this JSON:
{
"ResultSets": {
"Table1": [
{
"OrderID": 9518338,
"BasketID": 9518338,
"RefID": 65178176,
"SiteConfigID": 237
}
]
},
"OutputParameters": {}
}
Then I use a PARSE JSON action to get what looks like the same result, but now I'm told it's parsed and I can call variables.
Issue is when I try to call just, say, SiteConfigID, I get "The output you selected is inside a collection and needs to be looped over to be accessed. This action cannot be inside a foreach."
After some research, I know what's going on here. Table1 is an Array, and I need to tell PowerAutomate to just grab the first record of that array so it knows it's working with just a record instead of a full array. Fair enough. So I spin up a "Return Values to Virtual Power Agents" action just to see my output. I know I'm supposed to use a 'first' expression or a 'get [0] from array expression here, but I can't seem to make them work. Below are what I've tried and the errors I get:
Tried:
first(body('Parse-Sproc')?['Table1/SiteConfigID'])
Got: InvalidTemplate. Unable to process template language expressions in action 'Return_value(s)_to_Power_Virtual_Agents' inputs at line '0' and column '0': 'The template language function 'first' expects its parameter be an array or a string. The provided value is of type 'Null'. Please see https://aka.ms/logicexpressions#first for usage details.'.
Also Tried:
body('Parse-Sproc')?['Table1/SiteconfigID']
which just returns a null valued variable
Finally I tried
outputs('Parse-Sproc')?['Table1']?['value'][0]?['SiteConfigID']
Which STILL gives me a null-valued variable. It's the worst.
In that last expression, I also switched the variable type in the return to pva action to a string instead of a number, no dice.
Also, changed 'outputs' in that expression for 'body' .. also no dice
Here is a screenie of the setup:
To be clear: the end result i'm looking for is for the system to just return "SiteConfigID" as a string or an int so that I can pipe that into a virtual agent.
I believe this is what you need as an expression ...
body('Parse-Sproc')?['ResultSets']['Table1'][0]?['SiteConfigID']
You can see I'm just traversing down to the object and through the array to get the value.
Naturally, I don't have your exact flow but if I use your JSON and load it up into Parse JSON step to get the schema, I am able to get the result. I do get a different schema to you though so will be interesting to see if it directly translates.

Trying to get a JSONPath array of all leaves on json object

New to Go. My first project is to compare a NodeJS proxy and a Go proxy for account number tokenization. I have been doing NodeJS for a few years and am very comfortable with it. My proxies will not know the format of any request or response from the target servers. But it does have configurations coming from Redis/MongoDB that is similar to JSONPath expression. These configurations can change things like the target server/path, query parameters, headers, request body and response body.
For NodeJS, I am using deepdash's paths function to get an array of all the leaves in a JSON object in JSONPath format. I am using this array and RegEx to find my matching paths that I need to process from any request or response body. So far, it looks like I will be using gjson for my JSONPath needs, but it does not have anything for the paths command I was using in deepdash.
Will I need to create a recursive function to build this JSONPath array myself, or does anyone know of a library that will produce something similar?
for example:
{
"response": {
"results": [
{
"acctNum": 1234,
"someData": "something"
},
{
"acctNum": 5678,
"someData": "something2"
}
]
}
}
I will get an array back in the format:
[
"response.results[0].acctNum",
"response.results[0].someData",
"response.results[1].acctNum",
"response.results[1].someData"
]
and I can then use my filter of response.results[*].acctNum which translates to response\.results\[.*\]\.acctNum in Regex to get my acctNum fields.
From this filtered array, I should be able to use gjson to get the actual value, process it and then set the new value (I am using lodash in NodeJS)
There are a number of JSONPath implementations in GoLang. And I cannot really give a recommendation in this respect.
However, I think all you need is this basic path: $..*
It should return in pretty much any implementation that is able to return pathes instead of values:
[
"$['response']",
"$['response']['results']",
"$['response']['results'][0]",
"$['response']['results'][1]",
"$['response']['results'][0]['acctNum']",
"$['response']['results'][0]['someData']",
"$['response']['results'][1]['acctNum']",
"$['response']['results'][1]['someData']"
]
If I understand correctly this should still work using your approach filtering using RegEx.
Go SONPath implementations:
http://github.com-PaesslerAG-jsonpath
http://github.com-bhmj-jsonslice
http://github.com-ohler55-ojg
http://github.com-oliveagle-jsonpath
http://github.com-spyzhov-ajson
http://github.com-vmware-labs-yaml-jsonpath

go properly handling slices and strings

I am using goRequest http://parnurzeal.github.io/gorequest/ to make some HTTP requests against a server process I need to talk to. The authentication process works like this;
send in a GET request with an authentication header set. No problem there, but I need to grab a header from the response and use a returned value to reauthenticate each following request.
The retuned HTTP header looks like this.
Response headers
map[Location:[900767244] Content-Type:[application/xml] Date:[Fri, 18 Sep 2015 18:19:41 GMT] Server:[Apache] X-Frame-Options:[SAMEORIGIN] Set-Cookie:[JSESSIONID=D5C976F5646365FF030DBAD770DA774C; Path=/; Secure; HttpOnly]]
I need to get that location value as it's my session token going forward. If I grap it like this:
session, ok := response.Header["Location"]
if !ok {
fmt.Println("Did not receive a location header.")
}
fmt.Println("Session: ", session)
I can get it, but it's a slice and NOT a string. How can I get that value as a string so I can pop it back into my request headers going forward? As you can see in the following error:
./login.go:133: cannot use session (type []string) as type string in argument to logoutRequest.Delete
Thanks a lot!
Craig
If you want one value, use the Header's Get method
location := response.Header.Get("Location")
This also canonicalizes the header name for you, so that you still get a value even when using slightly different capitalization.
You only need to index an http.Header value directly when you need to get more than than the first possible value. If you want all values with a canonicalized header name, you can use textproto.CanonicalMIMEHeaderKey
vals := response.Header[textproto.CanonicalMIMEHeaderKey(header)]
The headers have location as an array, you just need to pass Location[0] to that method rather than simply Location because it is a slice. That being said, indexing into the array without checking the bounds is unsafe so you should probably do;
if len(Location) == 1 {
logoutRequest(Location[0])
} else {
// error state
}
One last thing to provide you guidance in the future. You can tell that the response headers object has a type more like this; map[string][]string (in reality that maybe []interface{} but I'm not certain from the output) by seeing that each item inside the outer brackets [] has it's values contained within another set of brackets, meaning it is an array that could contain 0 or more values.

Is there a Hash nil check for fetch?

I'm using the facebook graph-api and I'm fetching the checkin keys. Most places have this checkin key, but every once in a while I find a place with no checkins that does not have this key. Is there a way to check for nil and prevent the following code from blowing up my very large rake?
graph.get_object(5811874893).fetch("checkins")
KeyError: key not found: "checkins"
x = graph.get_object(x.place_id)["checkins"]
Based on feedback below I have the following error, with the same theme: nil checking. How would you handle this?
nil can't be coerced into Fixnum
Have you tried seeing if has_key? is available, or using graph.get_object(5811874893)["checkins"] rather than graph.get_object(5811874893).fetch("checkins")?
To return 0 if there isn't checkins, do
graph.get_object(5811874893).fetch("checkins") { 0 }
The block after fetch specifies what to do if "checkins" doesn't exist.
Why don't you just do
graph.get_object(5811874893)["checkins"]
If you access a hash using [], no error will be raised, and you'll get a nil value (as long as the hash doesn't set it to something else).
What you want is this:
graph.get_object(5811874893).fetch("checkins", 0)
fetch accepts a second optional param which represents the default value.

Resources