Convert struct to json array instead of json object - json

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

Related

How to filter out duplicates while unmarshalling json into struct?

I have this json which I am trying to unmarshall to my struct.
{
"clientMetrics": [
{
"clientId": 951231,
"customerData": {
"Process": [
"ABC"
],
"Mat": [
"KKK"
]
},
"legCustomer": [
8773
]
},
{
"clientId": 1234,
"legCustomer": [
8789
]
},
{
"clientId": 3435,
"otherIds": [
4,
32,
19
],
"legCustomer": [
10005
]
},
{
"clientId": 9981,
"catId": 8,
"legCustomer": [
13769
]
},
{
"clientId": 12124,
"otherIds": [
33,
29
],
"legCustomer": [
12815
]
},
{
"clientId": 8712,
"customerData": {
"Process": [
"College"
]
},
"legCustomer": [
951
]
},
{
"clientId": 23214,
"legCustomer": [
12724,
12727
]
},
{
"clientId": 119812,
"catId": 8,
"legCustomer": [
14519
]
},
{
"clientId": 22315,
"otherIds": [
32
],
"legCustomer": [
12725,
13993
]
},
{
"clientId": 765121,
"catId": 8,
"legCustomer": [
14523
]
}
]
}
I used this tool to generate struct as shown below -
type AutoGenerated struct {
ClientMetrics []ClientMetrics `json:"clientMetrics"`
}
type CustomerData struct {
Process []string `json:"Process"`
Mat []string `json:"Mat"`
}
type ClientMetrics struct {
ClientID int `json:"clientId"`
CustomerData CustomerData `json:"customerData,omitempty"`
LegCustomer []int `json:"legCustomer"`
OtherIds []int `json:"otherIds,omitempty"`
CatID int `json:"catId,omitempty"`
CustomerData CustomerData `json:"customerData,omitempty"`
}
Now my confusion is, I have lot of string or int array so how can I filter out duplicates? I believe there is no set data type in golang so how can I achieve same thing here? Basically when I unmarshall json into my struct I need to make sure there are no duplicates present at all. Is there any way to achieve this? If yes, can someone provide an example how to achieve this for my above json and how should I design my struct for that.
Update
So basically just use like this and change my struct definitions and that's all? Internally it will call UnmarshalJSON and take care of duplicates? I will pass json string and structure to JSONStringToStructure method.
func JSONStringToStructure(jsonString string, structure interface{}) error {
jsonBytes := []byte(jsonString)
return json.Unmarshal(jsonBytes, structure)
}
type UniqueStrings []string
func (u *UniqueStrings) UnmarshalJSON(in []byte) error {
var arr []string
if err := json.Unmarshal(in, arr); err != nil {
return err
}
*u = UniqueStrings(dedupStr(arr))
return nil
}
func dedupStr(in []string) []string {
seen:=make(map[string]struct{})
w:=0
for i:=range in {
if _,s:=seen[in[i]]; !s {
seen[in[i]]=struct{}{}
in[w]=in[i]
w++
}
}
return in[:w]
}
Ideally, you should post-process these arrays to remove duplicates. However, you can achieve this during unmarshaling using a custom type with an unmarshaler:
type UniqueStrings []string
func (u *UniqueStrings) UnmarshalJSON(in []byte) error {
var arr []string
if err:=json.Unmarshal(in,arr); err!=nil {
return err
}
*u=UniqueStrings(dedupStr(arr))
return nil
}
where
func dedupStr(in []string) []string {
seen:=make(map[string]struct{})
w:=0
for i:=range in {
if _,s:=seen[in[i]]; !s {
seen[in[i]]=struct{}{}
in[w]=in[i]
w++
}
}
return in[:w]
}
You may use a similar approach for []ints.
You use the custom types in your structs:
type CustomerData struct {
Process UniqueStrings `json:"Process"`
Mat UniqueStrings `json:"Mat"`
}

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

Manipulating mongodb's bson.D output format

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
}

Create struct for complex JSON array in Golang

I have the following JSON array that I'm trying to convert to a struct.
[
{
"titel": "test 1",
"event": "some value",
"pair": "some value",
"condition": [
"or",
[
"contains",
"url",
"/"
]
],
"actions": [
[
"option1",
"12",
"1"
],
[
"option2",
"3",
"1"
]
]
}, {
"titel": "test 2",
"event": "some value",
"pair": "some value",
"condition": [
"or",
[
"contains",
"url",
"/"
]
],
"actions": [
[
"option1",
"12",
"1"
],
[
"option2",
"3",
"1"
]
]
}
]
This is the struct I've got so far:
type Trigger struct {
Event string `json:"event"`
Pair string `json:"pair"`
Actions [][]string `json:"actions"`
Condition []interface{} `json:"condition"`
}
type Triggers struct {
Collection []Trigger
}
However, this does not really cover the "Condition" part. Ideally id like to have a structure for that as well.
Assuming that there can be only a single condition per item in the root array, you can try a struct below. This could make using Condition clear.
https://play.golang.org/p/WxFhBjJmEN
type Trigger struct {
Event string `json:"event"`
Pair string `json:"pair"`
Actions [][]string `json:"actions"`
Condition Condition `json:"condition"`
}
type Condition []interface{}
func (c *Condition) Typ() string {
return (*c)[0].(string)
}
func (c *Condition) Val() []string {
xs := (*c)[1].([]interface{})
ys := make([]string, len(xs))
for i, x := range xs {
ys[i] = x.(string)
}
return ys
}
type Triggers struct {
Collection []Trigger
}

Go structure for unmarshalling a JSON array

So I have some JSON (courtesy of the PetFinder API) that has a JSON array "pet". I want to unmarshal from it, using the "encoding/json" package, a slice of pet structs. What would this kind of structure look like? I can't find any examples of how the unmarshall function handles JSON arrays.
Here's what I was planning to do once I had a proper struct:
pfetch := new(PetsFetcher) // where PetsFetcher is the struct im asking for
err := json.Unmarshal(body, &pfetch)
And here's the json that is in body (in the form of a slice of ascii bytes):
{
"petfinder": {
"lastOffset": {
"$t": 5
},
"pets": {
"pet": [
{
"options": {
"option": [
{
"$t": "altered"
},
{
"$t": "hasShots"
},
{
"$t": "housebroken"
}
]
},
"breeds": {
"breed": {
"$t": "Dachshund"
}
}
},
{
"options": {
"option": {
"$t": "hasShots"
}
},
"breeds": {
"breed": {
"$t": "American Staffordshire Terrier"
}
},
"shelterPetId": {
"$t": "13-0164"
},
"status": {
"$t": "A"
},
"name": {
"$t": "HAUS"
}
}
]
}
}
}
Thanks in advance.
I really have no idea what those $t attributes are doing there in your JSON, so let’s answer your question with a simple example. To unmarshal this JSON:
{
"name": "something",
"options": [
{
"key": "a",
"value": "b"
},
{
"key": "c",
"value": "d"
},
{
"key": "e",
"value": "f"
},
]
}
You need this Data type in Go:
type Option struct {
Key string
Value string
}
type Data struct {
Name string
Options []Option
}
You can unmarshal a javascript array into a slice. The marhsal/unmarshalling rules are described under Marshal in the json package.
To unmarshal keys that look like "$t", you'll have to annotate the struct that it'll unpack into.
For example:
type Option struct {
Property string `json:"$t,omitempty"`
}
It may be that the $t that appear are a mistake, and are supposed to be keys in a dictionary.