I have a JSON array as follows. I am using cJSON to parse it. However, I want to check if a field exists in my array. I have tried to use cJSON_GetObjectItem and cJSON_IsNull and compare its return value to NULL to determine if a field does not exist.
But, that does not seem to work. Is there some other way to find out if a field is present or not (ex: age is not present in the second subarray)?
"people":
[
{
"name": "Mickey",
"age": 7
},
{
"name": "Susan"
}
]
To check if a key in json exists, can do something like:
cJSON *name=cJSON_GetObjectItem(root,"name");
cJSON *age=cJSON_GetObjectItem(root,"age");
if (name) printf("name: %s\n",name->valuestring);
if (age) printf("age: %s\n", age->valuestring);
cJSON *age=cJSON_GetObjectItem(root,"age");
if (cJSON_IsString(age) && (age->valuestring != NULL))
{
printf("The age is %s. \n", age->valuestring);
}
else
{
printf("The age is NULL. \n");
}
Related
I am having following data format:
{"time":"2022-08-24T06:00:00Z","duration":0,"level":"OK","data":{"series":[{"name":"gnb_kpi","tags":{"ID":"1017","_field":"Success_rate%","cluster_id":"ec17-1017","swversion":"6.0"},"columns":["time","_value"],"values":[["2022-08-24T06:00:00Z","100"]]}]},"previousLevel":"CRITICAL","recoverable":true}
I want to remove the _time field from the columns array and similarily the timestamp from values array. The output I want is like this:
{"time":"2022-08-24T06:00:00Z","duration":0,"level":"OK","data":{"series":[{"name":"gnb_kpi","tags":{"ID":"1017","_field":"Success_rate%","cluster_id":"ec17-1017","swVersion":"6.0"},"columns":["_value"],"values":[["100"]]}]},"previousLevel":"CRITICAL","recoverable":true}
I used the online service JSON-to-Go to generate a data structure that corresponds to your input. It produced
type AutoGenerated struct {
Time time.Time `json:"time"`
Duration int `json:"duration"`
Level string `json:"level"`
Data struct {
Series []struct {
Name string `json:"name"`
Tags struct {
ID string `json:"ID"`
Field string `json:"_field"`
ClusterID string `json:"cluster_id"`
Swversion string `json:"swversion"`
} `json:"tags"`
Columns []string `json:"columns"`
Values [][]interface{} `json:"values"`
} `json:"series"`
} `json:"data"`
PreviousLevel string `json:"previousLevel"`
Recoverable bool `json:"recoverable"`
}
The algorithm is simple:
parse JSON into the generated structure
iterate over series
find the position of time field in columns
remove the corresponding data elements from values
https://go.dev/play/p/rjnvmdBXCE4
Output is (beautified)
{
"time": "2022-08-24T06:00:00Z",
"duration": 0,
"level": "OK",
"data": {
"series": [
{
"name": "gnb_kpi",
"tags": {
"ID": "1017",
"_field": "Success_rate%",
"cluster_id": "ec17-1017",
"swversion": "6.0"
},
"columns": [
"_value"
],
"values": [
[
"100"
]
]
}
]
},
"previousLevel": "CRITICAL",
"recoverable": true
}
As you see, no time
model.go:
type First struct {
ID int `json:"id" gorm:"column:id;primary_key"`
Status string `json:"status" gorm:"column:status"`
SecondID int `json:"second_id" gorm:"column:second_id"`
SecondData Second `json:"second_data" gorm:"foreignKey:SecondID;references:ID"`
}
type Second struct {
ID int `json:"id" gorm:"column:second_id;primary_key"`
Status string `json:"status" gorm:"column:status"`
Description string `json:"description" gorm:"column:description"`
}
var res []model.First
db.Raw("first.*, second.* FROM first LEFT JOIN second ON first.second_id = second.second_id")
db.Preload("SecondData").Find(&res).Error
Output:
{
"id": 1,
"status": "A",
"second_id": 1
"second_data": {
"id": 1
"status": "B",
"description": "blablabla"
}
}
I don't really know how db.Preload() works. Why i should use db.Preload() to get "SecondData" every time i need do nested struct ? Are it's possible only use db.Row() or db.Table().Joins().Where().Find(), i mean's without db.Preload()?
If you want SecondData loaded every time when the First struct is loaded without using Preload, you might consider using hooks.
It might look something like this:
func (f *First) AfterFind(tx *gorm.DB) error {
return tx.First(&f.SecondData, f.SecondID).Error
}
So, when you load the First data, the AfterFind hook should be triggered.
So I have an Struct that holds data that has a AddedByUser which links to my User Struct.
What I want to be able to do it remove the UserLevel from the AddedByUser
Now I want to be able to do it from this function only, so using the json:"-" is not an option. That would remove it from all json output. I only want to remove it form this one function.
I should also say that these are Gorm models and when I have been trying to remove the 10 option (UserLevels) it only removes the outer data set not the UserLevel from all of the data.
{
"ID": 1,
"CreatedAt": "2019-01-08T16:33:09.514711Z",
"UpdatedAt": "2019-01-08T16:33:09.514711Z",
"DeletedAt": null,
"UUID": "00000000-0000-0000-0000-000000000000",
"Title": "title000",
"Information": "info999",
"EventDate": "2006-01-02T15:04:05Z",
"AddedByUser": {
"ID": 2,
"CreatedAt": "2019-01-08T15:27:52.435397Z",
"UpdatedAt": "2019-01-08T15:27:52.435397Z",
"DeletedAt": null,
"UUID": "b019df80-a7e4-4397-814a-795e7e84b4ca",
"Firstname": "Me",
"Surname": "admin",
"Password": "....",
"Email": "admin#email.co.uk",
"UserLevel": {
"ID": 0,
"CreatedAt": "0001-01-01T00:00:00Z",
"UpdatedAt": "0001-01-01T00:00:00Z",
"DeletedAt": null,
"LevelTitle": "",
"UserLevel": null
},
So this is what I have tried,
data := []models.MyData{}
data = append(data[0:2])
I have about 14 results, with out the append it loads all the results but with this is only loads two results. The idea was to remove either UpdateAt or Title. As I am not sure if the gorm model information is all 0 or if the slice sees them as 0,1,2,3,4 etc.
I have also tried to range over the slice of models, while I can access each of the sections, I can not seem to find a simple method to remove data by name from a struct? Maps seem to have that but not structs which I am not sure why?
Thanks.
UPDATE
This is the model I am using:
//Model
type MyData struct {
gorm.Model
UUID uuid.UUID
Title string
Information string
EventDate time.Time
AddedByUser Users `gorm:"ForeignKey:added_by_user_fk"`
AddedByUserFK uint
}
//Users Model
type Users struct {
gorm.Model
UUID uuid.UUID
Firstname string
Surname string
Password string
Email string
UserLevel UserLevels `gorm:"ForeignKey:user_level_fk" json:",omitempty"`
UserLevelFK uint
}
As mentioned in the comments, you cannot remove fields from a struct value, because that would yield a value of a different type.
However, you can set fields to their zero value. Combined with the omitempty JSON tag, you can exclude fields from the JSON encoding. To make this work properly, you have to change the UserLevel field to a pointer type (otherwise you end up with empty objects in the JSON document).
Types shortened for brevity:
package main
import (
"encoding/json"
"fmt"
)
type MyData struct {
Title string
AddedByUser Users
}
type Users struct {
ID int
UserLevel *UserLevels `json:",omitempty"` // pointer type with omitempty
}
type UserLevels struct {
LevelTitle string
}
func main() {
var x MyData
x.Title = "foo"
x.AddedByUser.ID = 2
x.AddedByUser.UserLevel = &UserLevels{}
f(x)
b, _ := json.MarshalIndent(x, "", " ")
fmt.Println("main:\n" + string(b))
}
func f(x MyData) {
// "unset" UserLevel. Since we are receiving a copy of MyData, this is
// invisible to the caller.
x.AddedByUser.UserLevel = nil
b, _ := json.MarshalIndent(x, "", " ")
fmt.Println("f:\n" + string(b))
}
// Output:
// f:
// {
// "Title": "foo",
// "AddedByUser": {
// "ID": 2
// }
// }
// main:
// {
// "Title": "foo",
// "AddedByUser": {
// "ID": 2,
// "UserLevel": {
// "LevelTitle": ""
// }
// }
// }
Try it on the playground: https://play.golang.org/p/trUgnYamVOA
Alternatively, you can define new types that exclude the AddedByUser field. However, since this field isn't at the top level, this is a lot of work, and it's easy to forget to update those types when new fields are added to the original types.
If the field were at the top level, the compiler would do most of the work for you, because types that only differ in their field tags can be directly converted to one another:
type MyData struct {
ID int
Title string
}
func main() {
var x MyData
x.ID = 1
x.Title = "foo"
f(x)
}
func f(x MyData) {
type data struct { // same as MyData, except the field tags
ID int
Title string `json:"-"`
}
b, _ := json.MarshalIndent(data(x), "", " ")
fmt.Println("main:\n" + string(b))
}
I have a json file (nested json) that I am unmarshalling its content into a map[string]interface. Now I have to implement pagination as the data is large. The client side will send as a query parameter the desired page, how can I slice the data I have?
This is a snippet of the data I am dealing with:
"packages":{
"pkg1": {
"meta": {
"description": "description1",
"name": "pkg1.1"
},
"name": "pkg1.1"
},
"pkg2": {
"meta": {
"description": "description2",
"name": "pkg2.2"
},
"name": "pkg2.2"
},
}
So what I did is that I recursively iterated through the data and created an array of a custom type containing the data I need (name, description) for each entry so that I can use it for pagination. Here is the code I used:
type Object struct {
name string
description string
}
func iterate(aMap map[string]interface{}, result *[]Object){
for key, val := range aMap {
switch val.(type) {
case map[string]interface{}:
if(key == "meta"){
switch reflect.TypeOf(val).Kind() {
case reflect.Map:
s := reflect.ValueOf(val)
var tmpData Object
if(s.MapIndex(reflect.ValueOf("name")).IsValid()){
tmpData.name = s.MapIndex(reflect.ValueOf("name")).Interface().(string)
}
if(s.MapIndex(reflect.ValueOf("description")).IsValid()){
tmpData.description = s.MapIndex(reflect.ValueOf("description")).Interface().(string)
}
*result = append(*result, tmpData)
}
}
iterate(val.(map[string]interface{}), result)
default: //DO NOTHING!!
}
}
}
If you're doing pagination, somewhere the data must be represented as a list instead of an object? I assume at some place in your JSON, you have a list of items, otherwise pagination doesn't make sense.
It shouldn't be very hard, something simple like this should work:
const (
itemsPerPage = 10
)
var data []map[string]interface{}
// pages start at 1, can't be 0 or less.
func GetDataPage(page int) []map[string]interface{} {
start := (page - 1) * itemsPerPage
stop := start + itemsPerPage
if start > len(data) {
return nil
}
if stop > len(data) {
stop = len(data)
}
return data[start:stop]
}
You are unmarshalling your json into a map which has no order by itself. In order to be able to paginate your results you need to order them in some way.
One way of doing it is to sort your data and then store it into an array. But in order to paginate you need to have ordered data and that is not possible with a map.
I've a struct :
type Talk struct {
Id string `gorethink:"id,omitempty"`
MatchId string
UserIdX string
UserIdY string
UserNameX string
UserNameY string
CreatedAt time.Time
}
My current Talk struct looks like this:
{
"CreatedAt": Wed Sep 14 2016 21:36:26 GMT+02:00 ,
"MatchId": "172d51fa-438b-49a5-bbe5-422377f09336" ,
"UserIdX": "acc4e0b6-d33b-4755-9c0a-ae5309c2ba75" ,
"UserIdY": "03f76d8b-ed6a-4c0f-9cde-27b17c9e7cdb" ,
"UserNameX": "Barbara" ,
"UserNameY": "Louis" ,
"id": "ead3f1b0-b242-4c6d-8027-a59572b58649"
}
How can I retrieve a talk, with a single query, where:
(UserIdX == talk.UserIdX AND UserIdY == talk.UserIdY) OR (UserIdX ==
talk.UserIdY AND UserIdY == talk.UserIdX)
I actually do it like the following:
func (talk *Talk) GetTalkByUsersId() bool {
talk1 := new(Talk)
talk2 := new(Talk)
curs, _ := r.Table("Talks").
Filter(r.Row.Field("UserIdX").Eq(talk.UserIdX)).
Filter(r.Row.Field("UserIdY").Eq(talk.UserIdY)).
Run(api.Sess)
curs2, _ := r.Table("Talks").
Filter(r.Row.Field("UserIdX").Eq(talk.UserIdY)).
Filter(r.Row.Field("UserIdY").Eq(talk.UserIdX)).
Run(api.Sess)
curs.One(&talk1)
curs2.One(&talk2)
if talk1.Id == "" && talk2.Id == "" {
return false
}
if talk1.Id != "" {
talk.copyTalk(talk1)
} else {
talk.copyTalk(talk2)
}
return true
}
How can I get this to work in a much simpler way?
I'm going to summon #daniel-cannon here but I think this is what you're looking for and will vastly simplify this query and reduce it to just a single query. But just two tips:
r.And and r.Or and their two use cases are very much your friends for doing some tricky logical conditions.
https://www.rethinkdb.com/api/javascript/#and
https://www.rethinkdb.com/api/javascript/#or
You can provide anonymous functions to .Filter to do things that would be kind of ugly with the simplified r.Row pattern.
r.Table("talks").Filter(func(talk r.Term) r.Term {
return r.Or(
r.And(talk.Field("UserIdX").Eq(UserIdX), talk.Field("UserIdX").Eq(UserIdY)),
r.And(talk.Field("UserIdY").Eq(UserIdX), talk.Field("UserIdX").Eq(UserIdY)),
)
}).Run(api.Sess)
I hope this helps!