Delphi json array without bracket - arrays

{
"code": 0,
"data": {
"KAVAUSDT": {
"name": "KAVAUSDT",
"min_amount": "0.5",
"maker_fee_rate": "0.003",
"taker_fee_rate": "0.003",
"pricing_name": "USDT",
"pricing_decimal": 4,
"trading_name": "KAVA",
"trading_decimal": 8
},
"CFXUSDT": {
"name": "CFXUSDT",
"min_amount": "5",
"maker_fee_rate": "0.003",
"taker_fee_rate": "0.003",
"pricing_name": "USDT",
"pricing_decimal": 6,
"trading_name": "CFX",
"trading_decimal": 8
},
... continue
}
}
If there were [ and ] symbols, I could solve it quickly with TJsonArray:
...
JsonArray := JsonValue.GetValue<TJSONArray>('data');
for ArrayElement in JsonArray do
begin
tempName := ArrayElement.GetValue<String>('name');
tempPricingName := ArrayElement.GetValue<String>('pricing_name');
...
end;
There are no [and ] symbols in this Json type.
Without the [ and ] symbols, I cannot access the data, as it is using a for loop.
Is there a simple solution?

There is no array in the JSON document you have shown. "KAVAUSDT", "CFXUSDT", etc are not array elements, they are simply named object fields of the "data" object. If you need to loop through the child fields of the "data" object, you can use TJSONObject (not TJSONArray!) for that, eg:
...
JsonObj := JsonValue.GetValue<TJSONObject>('data');
for Field in JsonObj do
begin
FieldObj := Field.JsonValue as TJSONObject;
tempName := FieldObj.GetValue<String>('name');
tempPricingName := FieldObj.GetValue<String>('pricing_name');
...
end;

Related

How to retrieve a nested array of objects in mongodb with golang?

I am using Golang/Fiber + Mongo driver.
I have simple struct for blog post:
type Post struct {
ID primitive.ObjectID `json:"_id" bson:"_id,omitempty"`
Title *string `json:"title" bson:"title"`
Slug *string `json:"slug" bson:"slug"`
Content []interface{} `json:"content" bson:"content,omitempty"` // same as any[]
CreatedAt time.Time `json:"created_at" bson:"created_at"`
UpdatedAt time.Time `json:"updated_at" bson:"updated_at"`
PublishedAt *time.Time `json:"published_at" bson:"published_at"`
}
In content I put array of objects:
[
{
data: 'blah blah'
},
{
data: 'blah blah'
}
]
Method itself is pretty straightforward:
func GetPostBySlug(slug string) (Post, error) {
post := Post{}
filter := bson.M{
"slug": slug,
}
database, error := Database.GetMongoDatabase()
if error != nil {
println(error)
}
collection := database.Collection(model_name)
err := collection.FindOne(ctx, filter).Decode(&post)
if err != nil {
return post, err
}
return post, nil
}
And this is what I get:
"_id": "000000000000000000000000",
"title": "Test",
"slug": "test",
"content": [ // array of arrays? what??
[
{
"Key": "data",
"Value": "blah blah"
},
{
"Key": "data",
"Value": "blah blah"
},
]
],
"created_at": "2022-06-07T21:08:04.261Z",
"updated_at": "2022-07-20T21:42:36.717Z",
"published_at": null
In mongodb the content field is saved exactly as I passed it, but when I am trying to get the document, the content transforms to this weird array of arrays with key-value pairs.
If I save something like:
content: [
{
data: {
test: 'test',
what: 'what'
}
}
]
It will transform to this:
content: [
[
Key: "data",
Value: [
{
Key: "test",
Value: "test"
},
{
Key: "what",
Value: "what"
},
]
]
]
I understand the reasons behind this (this is just golang's way to handle JSON?) and assume there should be an extra step somewhere in the middle, but I have no idea what exactly I need to do
Modify type of Content to interface{} or just string.
The reason for what you see is because you use interface{} for the element type of Content:
Content []interface{}
If you use interface{}, it basically carries no type information what type the driver should use when umarshaling the array elements, so the driver will choose / use bson.D to represent the documents of the content field. bson.D is a slice that holds the ordered list of fields of the documents, that's why you see an "array of arrays". Each bson.D is a slice, representing a document.
type D []E
type E struct {
Key string
Value interface{}
}
If you can model the array elements with a struct, use that, e.g.:
type Foo struct {
Bar string
Baz int
}
Content []Foo `json:"content" bson:"content,omitempty"`
// Or a pointer to Foo:
Content []*Foo `json:"content" bson:"content,omitempty"`
If you don't have a fixed model for the array elements, alternatively you may use bson.M which is a map (but the fields / properties will be unordered which may or may not be a problem):
type M map[string]interface{}
Using it:
Content []bson.M `json:"content" bson:"content,omitempty"`
#icza is correct. However, only use []bson.M if it's an array...I had to use bson.M instead because of the structuring of my data.

Go Struct JSON array of arrays

I´m having problems trying to mapping some data from specific Request Response because inside seriesLabels: there are data without property names (int,string) so I'm confused how to map that data:
This is the service response:
{
"data": {
"seriesLabels": [
[
0,
"(none)"
],
[
0,
"Cerveza"
],
[
0,
"Cigarros"
],
[
0,
"Tecate"
],
[
0,
"Cafe"
],
[
0,
"Amstel"
],
[
0,
"Leche"
],
[
0,
"Ultra"
],
[
0,
"Coca cola"
],
[
0,
"Agua"
]
]
}
}
My go struct to map that info is:
type PopularWord struct {
Data *Data `json:"data"`
}
type Data struct {
SeriesLabels []*SeriesLabels `json:"seriesLabels"`
}
type SeriesLabels struct {
value int32 `json:""`
name string `json:""`
}
What am I doing wrong? What is the correct way to declare the structs?
seriesLabels is a JSON array, and its elements are also JSON arrays.
To parse a JSON array of arrays, you may use a slice of slices in Go:
type Data struct {
SeriesLabels [][]interface{}
}
Testing it:
var pw *PopularWord
if err := json.Unmarshal([]byte(src), &pw); err != nil {
panic(err)
}
fmt.Println(pw)
for _, sl := range pw.Data.SeriesLabels {
fmt.Println(sl)
}
Output (try it on the Go Playground):
&{0xc000120108}
[0 (none)]
[0 Cerveza]
[0 Cigarros]
[0 Tecate]
[0 Cafe]
[0 Amstel]
[0 Leche]
[0 Ultra]
[0 Coca cola]
[0 Agua]
To get the inner arrays as struct values, you may implement custom unmarshaling:
type Data struct {
SeriesLabels []*SeriesLabels `json:"seriesLabels"`
}
type SeriesLabels struct {
value int32
name string
}
func (sl *SeriesLabels) UnmarshalJSON(p []byte) error {
var s []interface{}
if err := json.Unmarshal(p, &s); err != nil {
return err
}
if len(s) > 0 {
if f, ok := s[0].(float64); ok {
sl.value = int32(f)
}
}
if len(s) > 1 {
if s, ok := s[1].(string); ok {
sl.name = s
}
}
return nil
}
Testing code is the same, output (try this one on the Go Playground):
&{0xc0000aa0f0}
&{0 (none)}
&{0 Cerveza}
&{0 Cigarros}
&{0 Tecate}
&{0 Cafe}
&{0 Amstel}
&{0 Leche}
&{0 Ultra}
&{0 Coca cola}
&{0 Agua}
The problem is that SeriesLabels is represented in JSON as an array. If you want to use encoding/json, you have to implement the Unmarshaler interface to decode it (otherwise it will only accept JSON objects).
Fortunately, the code is simple:
func (s *SeriesLabels) UnmarshalJSON(d []byte) error {
arr := []interface{}{&s.value, &s.name}
return json.Unmarshal(d, &arr)
}
Note that this code ignores invalid input (array too long, or incorrect type). Depending on your needs, you may wish to add checks after the call to json.Unmarshal that the array length and contents (pointers) have not changed.
There are other JSON parsing libraries for Go that make this a bit less cumbersome, like gojay.

How to encode/marshal response from my server in multiple object JSON array

I am trying to format my response from a server in an array of multiple object JSON. I tried slice and []Struct but I believe I am not able to understand their usage correctly.
I tried the method mentioned here - https://play.golang.org/p/9OEPzbf0Me0 but didn't work in my case
I am using golang to get response for a query parameter
router.HandleFunc("/v1.0/singles", GetInfo).Methods("GET").Queries("Group", "{group}")
//controller.go
func SendToServer(jsonValue []byte, command string) (models.JSON, error) {
var pdatar models.JSON
s := []string{"http://172.xx.xxx.xx:xxxxx/", command}
response, err := http.Post(strings.Join(s, ""), "application/json", bytes.NewBuffer(jsonValue))
data, err := ioutil.ReadAll(response.Body)
err = json.Unmarshal(data, &pdatar)
mdatar := make(models.JSON)
mdatar["ID"] = pdatar["id"]
mdatar["Name"] = pdatar["name"]
fmt.Println(mdatar) //This is how it is coming map[ID:[877,235], Name:[FCU, GHP]]
/*Another attempt
var temp models.Arr
json.Unmarshal(data, &temp)
fmt.Println(temp) ---- returning null
*/
return mdatar, nil
}
func (c Controller) GetSingle(pdata models.Single) (models.JSON, error) {
mdata := make(models.JSON)
mdata["group"] = pdata.Group
jsonValue, err := json.Marshal(mdata)
pdatar, err := SendToServer(jsonValue, "func_info")
return pdatar, nil
// json.NewEncoder(w).Encode(pdatar) //sending this to my main function
}
//model.go
type JSON map[string]interface{}
type Single struct {
ID int `json:"id"`
Name string `json:"name"`
}
type Data []Single
type Arr [][]string
I have a response from my API in a format
{
"id":[877,235,312,429],
"name" ["FCU","GHP","c","d"],
"group":["x","x","y","z"]
}
With Current Code I am receiving a response
{"id":[877 235], "name": ["FCU" "GHP"]
Though I am expecting a response
{
"data":
[
{"id": 877
"name" : "FCU"
}
{"id": 235
"name": "GHP"
}
]
}
Your mdatarr doesn't change the format any, so this result makes sense.
If the data received from the server is in the format:
{
"id": [877,235,312,429],
"name" ["FCU","GHP","c","d"],
"group": ["x","x","y","z"]
}
then you will need translate it. Ideally you'd fix the server so that it sends data in the format:
{
{
"id": 877,
"name": "FCU",
"group": "x",
},
...
}
If the server could send something like the above, then you could simply unmarshal it into a models.Data object.
If that is not an option, and you really need to do the translation client-side, then you'll need a for loop that does something like this:
ids := pdatarr["id"]
names := pdatarr["name"]
...
if len(ids) != len(names) {
return nil, errors.New("Invalid input format")
}
var mdatar models.Data
for i := range(ids) {
mdatar = append(mdatar, models.Single{ids[i], names[i], ...})
}
return models.JSON{"data": mdatar}, nil

Get array objects with specific ObjectId in nested array

I'm trying to get a specific array of objects depending on ObjectId they have.
Here is my MongoDB database:
{
"_id" : ObjectId("59edb571593904117884b721"),
"userids" : [
ObjectId("59edb459593904117884b71f")
],
"macaddress" : "MACADDRESS",
"devices" : [ ],
"projectorbrand" : "",
}
{
"_id" : ObjectId("59edb584593904117884b722"),
"userids" : [
ObjectId("59edb459593904117884b71f"),
ObjectId("59e4809159390431d44a9438")
],
"macaddress" : "MACADDRESS2",
"devices" : [ ],
"projectorbrand" : "",
}
The command in MongoDB is:
db.getCollection('co4b').find( {
userids: { $all: [ ObjectId("59edb459593904117884b71f") ] }
} )
This will work and will return an array filtered correctly.
I would like to translate this query in Golang.
Here is my code:
pipe := bson.M{"userids": bson.M{"$all": objectId}}
var objects[]models.Objects
if err := uc.session.DB("API").C("objects").Pipe(pipe).All(&objects); err != nil {
SendError(w, "error", 500, err.Error())
} else {
for i := 0; i < len(objects); i++ {
objects[i].Actions = nil
}
uj, _ := json.MarshalIndent(objects, "", " ")
SendSuccessJson(w, uj)
}
I'm getting error like wrong type for field (pipeline) 3 != 4. I saw that $all needs string array but how to filter by ObjectId instead of string?
Thanks for help
You are attempting to use the aggregation framework in your mgo solution, yet the query you try to implement does not use one (and does not need one).
The query:
db.getCollection('co4b').find({
userids: {$all: [ObjectId("59edb459593904117884b71f")] }
})
Can simply be transformed to mgo like this:
c := uc.session.DB("API").C("objects")
var objects []models.Objects
err := c.Find(bson.M{"userids": bson.M{
"$all": []interface{}{bson.ObjectIdHex("59edb459593904117884b71f")},
}}).All(&objects)
Also note that if you're using $all with a single element, you can also implement that query using $elemMatch, which in MongoDB console would like this:
db.getCollection('co4b').find({
userids: {$elemMatch: {$eq: ObjectId("59edb459593904117884b71f")}}
})
Which looks like this in mgo:
err := c.Find(bson.M{"userids": bson.M{
"$elemMatch": bson.M{"$eq": bson.ObjectIdHex("59edb459593904117884b71f")},
}}).All(&objects)

Convert json single element arrays to strings

In Go I have to parse this json:
{
"response": [
{
"message": [
"hello world"
],
"misc": [
{
"timestamp": [
"2017-06-28T05:52:39.347Z"
],
"server": [
"server-0101"
]
}
]
}
]
}
I'd like to get an object in Go that doesn't include all the unnecessary arrays of with a single string. The source json will never have more than one string in each array.
So the end result that I'd like to get would be this json:
{
"response": {
"message": "hello world",
"misc": {
"timestamp": "2017-06-28T05:52:39.347Z",
"server": "server-0101"
}
}
}
Or an equivalent object in Go.
Right now I have to use Response[0].Misc[0].Timestamp[0] to access the data which seems weird.
You can override the default behaviour of json.Marshal / json.Unmarshal methods for a struct, by defining its own MarshalJSON or UnmarshalJSON properly.
Here there is an excerpt for the code of a simplified version of the struct you need to decode.
type Response struct {
Message string `json:"message"`
}
// UnmarshalJSON overrides the default behaviour for JSON unmarshal method.
func (r *Response) UnmarshalJSON(data []byte) error {
auxResponse := &struct {
Message []string `json:"message"`
}{}
if err := json.Unmarshal(data, &auxResponse); err != nil {
return err
}
// Consider to add some checks on array length :)
r.Message = auxResponse.Message[0]
return nil
}
You can access the full working example here.
I suggest you to read this interesting article about custom JSON encode/decode with golang.
I'd like to get an object in Go that doesn't include all the unnecessary arrays of with a single string.
The hard way: Parse the JSON by hand (write our own parser).
The sensible way: Unmarshal via package encoding/json into some Go type matching the JSON or into some generic interface{} and copy the pieces into a different, simpler Go type afterwards.
Creating your own unmarshaller is probably best, but this is a quick way to simulate what you want to achieve.
package main
import (
"encoding/json"
"fmt"
)
// JSON ...
var JSON = `{
"response": [
{
"message": [
"hello world"
],
"misc": [
{
"timestamp": [
"2017-06-28T05:52:39.347Z"
],
"server": [
"server-0101"
]
}
]
}
]
}
`
type rawObject struct {
Response []struct {
Message []string `json:"message"`
Misc []interface{} `json:"misc"`
} `json:"response"`
}
type clean struct {
Message string `json:"message"`
Misc map[string]interface{} `json:"misc"`
}
func main() {
var o rawObject
var c clean
// init map
c.Misc = make(map[string]interface{})
// unmarshall the raw data
json.Unmarshal([]byte(JSON), &o)
for _, res := range o.Response { // I assume there should only be one response, don't know why this is wrapped as an array
// assume message is not really an array
c.Message = res.Message[0]
// convert []interface to map[string]interface
for _, m := range res.Misc {
for k, v := range m.(map[string]interface{}) {
c.Misc[k] = v
}
}
}
fmt.Printf("%+v\n", c)
}
What i don't like about this answer is that it isn't very reusable..so a function should probably be made and more error checking (part of creating a custom unmarshaller). If this were used in heavy production it might run into some memory issues, as I have to create a raw object to create a clean object.. but as a one off script it does the job. I my clean struct doesn't add response as a type because i find it to be redundant.

Resources