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.
Related
{
"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;
I am trying to parse given API response into a struct.
It seems to be an array.
[
{
"host_name" : "hostname",
"perf_data" : "",
"plugin_output" : "All good",
"state" : 0
}
]
I cannot figure out how to create struct for it, I came up with:
type ServiceData struct {
HostName string `json:"host_name"`
PerfData string `json:"perf_data"`
PluginOutput string `json:"plugin_output"`
State int `json:"state"`
}
defer resp.Body.Close()
buf := new(bytes.Buffer)
buf.ReadFrom(resp.Body)
jsonStr := buf.String()
var servicedata ServiceData
json.Unmarshal([]byte(jsonStr), &servicedata)
But no luck.
Should I perhaps remove square brackets from the initial response?
Could somebody point me in the right direction?
You may unmarshal JSON arrays into Go slices. So unmarshal into a value of type []ServiceData or []*ServiceData:
var servicedata []*ServiceData
A working demo:
func main() {
var result []*ServiceData
if err := json.Unmarshal([]byte(src), &result); err != nil {
panic(err)
}
fmt.Printf("%+v", result[0])
}
const src = `[
{
"host_name" : "hostname",
"perf_data" : "",
"plugin_output" : "All good",
"state" : 0
}
]`
Which outputs (try it on the Go Playground):
&{HostName:hostname PerfData: PluginOutput:All good State:0}
Also note that you may unmarshal "directly from the body", no need to read the body first.
Use json.Decoder to do that:
var servicedata []*ServiceData
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
// handle error
}
It's much shorter, easier to read and it's more efficient.
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
I have an array (myArray) of custom objects (MyObject). Each object in the array connects to at least one other object in the array (see the code below). I'm trying to find a way to determine if all of the objects in myArray are connected to one another.
class MyObject {
var id: Int = 0
var connectedIds: [Int] = []
}
If myArray contains 5 elements (A, B, C, D, E; A.id = 0, B.id = 1, ...), I want to determine if all five are connected in some way. Each object has an array called connectedIds of the ids of the objects it's connected to (it does not include itself).
For example, this would be valid:
print(A.connectedIds) // [1]
print(B.connectedIds) // [0, 2]
print(C.connectedIds) // [1, 4]
print(D.connectedIds) // [4]
print(E.connectedIds) // [2, 3]
...but this would not:
print(A.connectedIds) // [1]
print(B.connectedIds) // [0]
print(C.connectedIds) // [3, 4]
print(D.connectedIds) // [2, 4]
print(E.connectedIds) // [2, 3]
Viewed graphically (ignore the red circle), this is okay:
But this is not:
Algorithme is based on find a route between two points, and another to check if all points can be connected (having route between them):
// Find route from an object to other object
func findNewRoute(currentRoute: inout [Int], from: Int, to: Int) {
currentRoute.append(from)
let connectedObjects = (objects.filter { $0.id == from }.first!).connectedIds
for index in connectedObjects {
if !currentRoute.contains(index) {
findNewRoute(currentRoute: ¤tRoute, from: index, to: to)
}
}
}
// Check Validation
func checkValid(group: [MyObject]) -> Bool {
for object in objects {
if !(objects.filter {
element in
var result:[Int] = []
findNewRoute(currentRoute: &result, from: object.id, to: element.id)
return !result.contains(element.id)
}).isEmpty {
return false
}
}
return true
}
This is a graph problem of a connected component.
You can run the depth-first search on each node and get the connected component. If you have more than two component in the result then yes all the object is not connected to all other objects.
class MyObject {
var id: Int = 0
var connectedIds: [Int] = []
}
func isAlreadyVisited(n: MyObject, cc: inout [[MyObject]]) -> Bool {
for c in cc {
c.forEach {
return ($0.id == n.id)
}
}
return false
}
func connectedComponent(g: [MyObject]) -> [[MyObject]] {
var cc = [[MyObject]]()
for n in g {
if !isAlreadyVisited(n: n, cc: &cc) {
var c = DFS(n, g) // Use placeHolder for DFS, you can write your own
cc.append(contentsOf: c)
}
}
return cc
}
var cc = connectedComponent(g: [MyObject(), MyObject()])
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.