Manipulating mongodb's bson.D output format - json

Im using FindOne to query one row of data (single document):
package main
import (
"context"
"fmt"
"github.com/fatih/color"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
func mongoDbFindOne(key, value string) bson.D {
var result bson.D
_ = Collection.FindOne(context.TODO(), bson.D{{key, value}}).Decode(&result)
color.Green("[+] Found: %+v\n", result)
return result
}
And this a small part of how the result is shown:
[
{
"Key": "_id",
"Value": "1600540844649"
},
{
"Key": "hostname",
"Value": "DESKTOP-xxxxxx"
},
{
"Key": "cmdLine",
"Value": []
},
{
"Key": "pid",
"Value": 4816
}
]
But this how i want it to be:
[
{
"_id": "1600540844649"
},
{
"hostname": "DESKTOP-xxxxxx"
},
{
"cmdLine": []
},
{
"pid": 4816
}
]
Or:
[
{
"_id": "1600540844649",
"hostname": "DESKTOP-xxxxxx",
"cmdLine": [],
"pid": 4816,
}
]
What should i do? I have searched through SO and google but no luck. Should i use struct or creating any objects? I also searched for saving/converting bson to json but there is solution to it.

I found the solution myself:
Using bson.M instead of bson.D solved my issue:
import (
"context"
"fmt"
"github.com/fatih/color"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
func mongoDbFindOne(key, value string) bson.M {
var result bson.M
_ = Collection.FindOne(context.TODO(), bson.M{key:value}).Decode(&result)
color.Green("[+] Found: %+v\n", result)
return result
}

Related

Golang iterate over nested struct

i want to iterate over a nested json struct in golang. The issue is, i do not exactly know how nested the structure will be, becuase there are multiple jsons. In this case for example the output should be:
"available": false
"type": "foo"
"name": "foo"
"street": "foo"
....
Is that possible?
{
"informations": {
"available": false,
"provide": {
"informations": {
"customer": {
"type": "foo",
"address": {
"name": "foo",
"street": "foo",
"zipcode": "123",
"city": "foo"
}
}
}
}
}
}
I think you can do this with a map[string]interface{}. Because, as you said that you don't know exactly the structure of the JSON. But, again, you need to know the JSON structure each time while extracting data. Also, you need to do type assertion for inner map[string]interface{}. You can look at the answer from here for type assertion.
Here's the example code that I wrote:
package main
import (
"encoding/json"
"fmt"
)
var j = `{
"informations": {
"available": false,
"provide": {
"informations": {
"customer": {
"type": "foo",
"address": {
"name": "foo",
"street": "foo",
"zipcode": "123",
"city": "foo"
}
}
}
}
}
}`
func main() {
var data map[string]interface{}
err := json.Unmarshal([]byte(j), &data)
if err != nil {
fmt.Println(err.Error())
return
}
fmt.Println(data["informations"])
information := data["informations"]
provide := information.(map[string]interface{})["provide"]
fmt.Println(provide)
}
Output will be:
map[available:false provide:map[informations:map[customer:map[address:map[city:foo name:foo street:foo zipcode:123] type:foo]]]]
map[informations:map[customer:map[address:map[city:foo name:foo street:foo zipcode:123] type:foo]]]
Go Playground

How to create hierarchical json structure by slice string path

I'm building a backend for a file manager written in Go.
I'm looking for a way to make an example slice string path.
sliceStr := []string{
"/file_A.txt",
"/folder_B/file_B.txt",
"/folder_C/sub_folder_C/file_C.txt",
"/folder_C/sub_folder_C/file_C_2.txt",
}
to create an api that returns a hierarchical json structure to pass to the frontend using Devextreme FileManager. Like below
[
{
"name": "file_A.txt",
"isDirectory": false
},
{
"name": "folder_B",
"isDirectory": true,
"items": [
{
"name": "file_B.txt",
"isDirectory": false
}
]
},
{
"name": "folder_C",
"isDirectory": true,
"items": [
{
"name": "sub_folder_C",
"isDirectory": true,
"items": [
{
"name": "file_C.txt",
"isDirectory": false,
},
{
"name": "file_C_2.txt",
"isDirectory": false
}
]
}
]
}
]
you can try this method
join the slice into a byte array
Implement you own json UnmarshalJSON
type Yourstruct struct {
.......
}
func (t *Yourstruct ) UnmarshalJSON(data []byte) error
demo case

hcl, json, go : how can I iterate JSON?

Problem: I’m trying to iterate through JSON-content and present the result like key,value pairs.
I've written some code that read hcl-files, these are then decoded with hcldec.Decode, and the result is then converted to JSON. These hcl-files define source and target for the application like this:
source.hcl:
source json "namefile" {
attr firstName {
type = "varchar"
expr = "$.firstName"
length = "30"
}
attr lastName {
type = "varchar"
expr = "$.lastName"
length = "40"
}
attr gender {
type = "varchar"
expr = "$.gender"
length = "10"
}
attr age {
type = "varchar"
expr = "$.age"
length = "2"
}
}
target.hcl
target table {
cols firstName {
name=source.json.namefile.attr.firstName.expr
type=source.json.namefile.attr.firstName.type
length=source.json.namefile.attr.firstName.length
}
cols lastName {
name=source.json.namefile.attr.lastName.expr
type=source.json.namefile.attr.lastName.type
length=source.json.namefile.attr.lastName.length
}
}
The decoding is done like this:
tspec := hcldec.ObjectSpec{
"target": &hcldec.BlockMapSpec{
TypeName: "target",
LabelNames: []string{"table"},
Nested: hcldec.ObjectSpec{
"cols": &hcldec.BlockMapSpec{
TypeName: "cols",
LabelNames: []string{"name"},
Nested: &hcldec.ObjectSpec{
"name": &hcldec.AttrSpec{
Name: "name",
Type: cty.String, //cty.List(cty.String),
Required: false,
},
"type": &hcldec.AttrSpec{
Name: "type",
Type: cty.String, //cty.List(cty.String),
Required: false,
},
"length": &hcldec.AttrSpec{
Name: "length",
Type: cty.String, //cty.List(cty.String),
Required: false,
},
},
},
},
},
}
targ, _ := hcldec.Decode(body, tspec, &hcl.EvalContext{
Variables: map[string]cty.Value{
"source": val.GetAttr("source"),
},
Functions: nil,
})
j := decodeCtyToJson(targ, true)
log.Debugf("targ -j (spec): %s", string(j)) // debug info
Where the decodeCtyToJson return []byte like this:
func decodeCtyToJson(value cty.Value, pretty bool) []byte {
jsonified, err := ctyjson.Marshal(value, cty.DynamicPseudoType)
if err != nil {
log.Debugf("Error: #v", err)
return nil
}
if pretty {
return jsonPretty.Pretty(jsonified)
}
return jsonified
}
Now, when I'm trying to testprint the JSON-content I'm not getting what I'm looking for:
var result map[string]interface{}
json.Unmarshal(j, &result)
log.Debugf("result: %# v", result)
tgtfil := result["value"].(map[string]interface{})
log.Debugf("tgtfil: %v", tgtfil)
log.Debugf("len(tgtfil): %# v", len(tgtfil))
for key, value := range tgtfil {
log.Debugf("key: %# v", key)
log.Debugf("value: %# v", value)
}
I am trying to get key, value pairs. But I'm getting this (first the whole JSON pretty print as wanted, then I am trying to loop through the JSON):
DEBU[0000] targ -j (spec): {
"value": {
"target": {
"table": {
"cols": {
"firstName": {
"length": "30",
"name": "$.firstName",
"type": "varchar"
},
"lastName": {
"length": "40",
"name": "$.lastName",
"type": "varchar"
}
}
}
}
},
"type": [
"object",
{
"target": [
"map",
[
"object",
{
"cols": [
"map",
[
"object",
{
"length": "string",
"name": "string",
"type": "string"
}
]
]
}
]
]
}
]
}
DEBU[0000] result: map[string]interface {}{"type":[]interface {}{"object", map[string]interface {}{"target":[]interface {}{"map", []interface {}{"object", map[string]interface {}{"cols":[]interface {}{"map", []interface {}{"object", map[string]interface {}{"length":"string", "name":"string", "type":"string"}}}}}}}}, "value":map[string]interface {}{"target":map[string]interface {}{"table":map[string]interface {}{"cols":map[string]interface {}{"firstName":map[string]interface {}{"length":"30", "name":"$.firstName", "type":"varchar"}, "lastName":map[string]interface {}{"length":"40", "name":"$.lastName", "type":"varchar"}}}}}}
DEBU[0000] tgtfil: map[target:map[table:map[cols:map[firstName:map[length:30 name:$.firstName type:varchar] lastName:map[length:40 name:$.lastName type:varchar]]]]]
DEBU[0000] len(tgtfil): 1
DEBU[0000] key: "target"
DEBU[0000] value: map[string]interface {}{"table":map[string]interface {}{"cols":map[string]interface {}{"firstName":map[string]interface {}{"length":"30", "name":"$.firstName", "type":"varchar"}, "lastName":map[string]interface {}{"length":"40", "name":"$.lastName", "type":"varchar"}}}}
Process finished with exit code 0
My aim here is to eventually be able to iterate through alle the attributes defined in the target.hcl (length, name and type for each cols in this case). Then generate DDL-code from this information and finally implement the DDL in e.g. Presto.
But as of now I’m not able to isolate this information.
Any pointers on how to do this is appreciated.
Thanks,
/b
The solution for me was to create a struct for the target and not use the target spec.

Convert struct to json array instead of json object

I apologise in advance if this question is considered too easy or whatever; this is the first time I'm writing anything in go. I have two structs (simplified for this question)
type A struct {
Content string
}
type B struct {
Element A `json:"0"`
Children []B `json:"1"`
}
I want to encode a value of type B into JSON, but instead of returning an object it should return a json array
For example:
What I get:
[
{
"0":{
"Content":"AAA"
},
"1":[
{
"0":{
"Content":"BBB"
},
"1":[
{
"0":{
"Content":"CCC"
},
"1":[]
},
{
"0":{
"Content":"DDD"
},
"1":[]
}
}
]
]
}
]
What I need:
[
{"Content": "AAA"},
[
[
{"Content": "BBB"},
[
{"Content": "CCC"},
[]
]
],
[
{"Content": "DDD"},
[]
]
]
]
I could do this by manually iterating through it, but I would hope that there's an integrated way to do it
You may do so by implementing json.Marshaler interface in B.
For example: https://play.golang.org/p/fT1WlQ5Raz
package main
import (
"encoding/json"
"fmt"
)
type A struct {
Content string
}
type B struct {
Element A
Children []B
}
// MarshalJSON implements json.Marshaler
func (b B) MarshalJSON() ([]byte, error) {
return json.Marshal([]interface{}{
b.Element,
b.Children,
})
}
func main() {
root := B{
Element: A{Content: "AAA"},
Children: []B{
B{
Element: A{Content: "BBB"},
Children: []B{
B{Element: A{Content: "CCC"}, Children: []B{}},
B{Element: A{Content: "DDD"}, Children: []B{}},
},
},
},
}
content, _ := json.MarshalIndent(root, "", " ")
fmt.Printf("%s\n", content)
}
Results:
[
{
"Content": "AAA"
},
[
[
{
"Content": "BBB"
},
[
[
{
"Content": "CCC"
},
[]
],
[
{
"Content": "DDD"
},
[]
]
]
]
]
]
i think you need a slice of interface to wrap both A and B struct into a single slice
please take a look at my snippet here https://play.golang.org/p/c0xldskKyz

jsonparser access fields by index

i have problem to access fields by index. this library https://github.com/buger/jsonparser
example from https://github.com/buger/jsonparser:
// Or use can access fields by index!
jsonparser.GetInt("person", "avatars", "[0]", "url")
My code:
package main
import (
"github.com/buger/jsonparser"
"fmt"
)
func main () {
data := []byte(`{
"person": {
"name": {
"first": "Leonid",
"last": "Bugaev",
"fullName": "Leonid Bugaev"
},
"github": {
"handle": "buger",
"followers": 109
},
"avatars": [
{
"url": "https://avatars1.githubusercontent.com/u/14009?v=3&s=460",
"type": "thumbnail"
}
]
},
"company": {
"name": "Acme"
}
}`)
fmt.Println(jsonparser.GetInt(data, "person", "[2]", "[0]", "url"))
}
result in terminal:
0 Key path not found
Person is not an array so you can not access it by index.