Hi guys I am trying to learn golang I am creating my own project which requires to create structure that I have hard times to write and initalized. I would be greatful if anyone can help me out with it.
{
"name":"message",
"args":[
{
"method":"joinChannel",
"params":{
"channel":"CHANNEL",
"name":"USERNAME",
"token":"XXXX",
"isAdmin":false
}
}
]
}
I was looking for some examples on google but only thing I could find was easie ones. This is what I came up
type Channel struct {
Name string `json:"name"`
Args []struct {
Method string `json:"method"`
Params struct {
Channel string `json:"channel"`
Name string `json:"name"`
Token string `json:"token"`
Isadmin bool `json:"isAdmin"`
} `json:"params"`
} `json:"args"`
}
Is there more transparent way to do it?
If you wanted to break the types out rather than having those anonymous declarations inline it would look like this;
type Channel struct {
Name string `json:"name"`
Args []Arg `json:"args"`
}
type Arg struct {
Method string `json:"method"`
Params Params `json:"params"`
}
type Params struct {
Channel string `json:"channel"`
Name string `json:"name"`
Token string `json:"token"`
Isadmin bool `json:"isAdmin"`
}
myChan := Channel{"Name", []Arg{ Arg{"Method", Params{ "Channel", "Name", "Token", true } } } }
You can separate nested struct like this.
http://play.golang.org/p/ghcMuFOdQC
Related
This question already has answers here:
panic: json: cannot unmarshal array into Go value of type main.Structure
(3 answers)
Closed 3 years ago.
I am building an application that reads information from the London Underground API. I'm struggling on parsing the GET request into something readable and where the user can access specific line information.
Here is my current code, I'm using a struct to store the response from the GET request after Unmarshaling it.
// struct for decoding into a structure
var tubeStatuses struct {
object []struct {
typeDef []string `json:"$type"`
idName string `json:"id"`
name string `json:"name"`
modeName string `json:"modeName"`
disruptions string `json:"disruption"`
created string `json:"created"`
modified string `json:"modified"`
statusObject []struct {
zeroObject []struct {
typeDef string `json:"$type"`
id int `json:"id"`
statusSeverity int `json:"statusSeverity"`
statusDesc string `json:"statusSeverityDescription"`
created string `json:"created"`
validity string `json:"validityPeriods"`
}
}
route string `json:"routeSections"`
serviceObject []struct {
zeroObject []struct {
typeDef string `json:"$type"`
name string `json:"name"`
uri string `json:"uri"`
}
}
crowdingObject []struct {
typeDef string `json:"$type"`
}
}
}
fmt.Println("Now retrieving Underground line status, please wait...")
// two variables (response and error) which stores the response from e GET request
getRequest, err := http.Get("https://api.tfl.gov.uk/line/mode/tube/status")
fmt.Println("The status code is", getRequest.StatusCode, http.StatusText(getRequest.StatusCode))
if err != nil {
fmt.Println("Error!")
fmt.Println(err)
}
//close - this will be done at the end of the function
// it's important to close the connection - we don't want the connection to leak
defer getRequest.Body.Close()
// read the body of the GET request
rawData, err := ioutil.ReadAll(getRequest.Body)
if err != nil {
fmt.Println("Error!")
fmt.Println(err)
}
jsonErr := json.Unmarshal(rawData, &tubeStatuses)
if jsonErr != nil {
fmt.Println(jsonErr)
}
//test
fmt.Println(tubeStatuses.object[0].name)
fmt.Println("Welcome to the TfL Underground checker!\nPlease enter a number for the line you want to check!\n0 - Bakerloo\n1 - central\n2 - circle\n3 - district\n4 - hammersmith & City\n5 - jubilee\n6 - metropolitan\n7 - northern\n8 - piccadilly\n9 - victoria\n10 - waterloo & city")
The error I see is the following:
json: cannot unmarshal array into Go value of type struct { object []struct { typeDef []string "json:\"$type\""; idName string "json:\"id\""; name string "json:\"name\""; modeName string "json:\"modeName\""; disruptions string "json:\"disruption\""; created string "json:\"created\""; modified string "json:\"modified\""; statusObject []struct { zeroObject []struct { typeDef string "json:\"$type\""; id int "json:\"id\""; statusSeverity int "json:\"statusSeverity\""; statusDesc string "json:\"statusSeverityDescription\""; created string "json:\"created\""; validity string "json:\"validityPeriods\"" } }; route string "json:\"routeSections\""; serviceObject []struct { zeroObject []struct { typeDef string "json:\"$type\""; name string "json:\"name\""; uri string "json:\"uri\"" } }; crowdingObject []struct { typeDef string "json:\"$type\"" } } }
How do I unmarshal the array into something readable?
Below is a working version of your code. However, there are multiple issues you'll want to address.
As mentioned elsewhere in this thread, you should make TubeStatuses (note capitalization) a type and an array. The field names should also be capitalized so that they are exported.
You weren't accounting for all of the output and in some cases the type was wrong. For example, you were missing an object called "Disruption". I've added it in my example. "Crowding" declaration was of the wrong type. Also, "route" aka "routeSections" is not a string. It's another array, likely of objects, but there was no output from the API for me to determine what routeSections actually consists of. So, as a workaround, I declared that as a type of "json.RawMessage" to allow it to be unmarshalled as a byte slice. See this for more details: https://golang.org/pkg/encoding/json/#RawMessage
You can't use json: "$type"`. Specifically, the dollar sign isn't allowed.
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)
type TubeStatuses struct {
TypeDef []string `json:"type"`
IDName string `json:"id"`
Name string `json:"name"`
ModeName string `json:"modeName"`
Disruptions string `json:"disruption"`
Created string `json:"created"`
Modified string `json:"modified"`
LineStatuses []struct {
Status []struct {
TypeDef string `json:"type"`
ID int `json:"id"`
StatusSeverity int `json:"statusSeverity"`
StatusSeverityDescription string `json:"statusSeverityDescription"`
Created string `json:"created"`
ValidityPeriods []struct {
Period struct {
TypeDef string `json: "type"`
FromDate string `json: "fromDate"`
ToDate string `json: "toDate"`
IsNow bool `json: "isNow"`
}
}
Disruption struct {
TypeDef string `json: "type"`
Category string `json: "category"`
CategoryDescription string `json: "categoryDescription"`
Description string `json: "description"`
AdditionalInfo string `json: "additionalInfo"`
Created string `json: "created"`
AffectedRoutes []string `json: "affectedRoutes"`
AffectedStops []string `json: "affectedStops"`
ClosureText string `json: closureText"`
}
}
}
RouteSections json.RawMessage `json: "routeSections"`
ServiceTypes []struct {
Service []struct {
TypeDef string `json:"type"`
Name string `json:"name"`
URI string `json:"uri"`
}
}
Crowding struct {
TypeDef string `json:"type"`
}
}
func main() {
fmt.Println("Now retrieving Underground line status, please wait...")
// two variables (response and error) which stores the response from e GET request
getRequest, err := http.Get("https://api.tfl.gov.uk/line/mode/tube/status")
if err != nil {
fmt.Println("Error!")
fmt.Println(err)
}
fmt.Println("The status code is", getRequest.StatusCode, http.StatusText(getRequest.StatusCode))
//close - this will be done at the end of the function
// it's important to close the connection - we don't want the connection to leak
defer getRequest.Body.Close()
// read the body of the GET request
rawData, err := ioutil.ReadAll(getRequest.Body)
if err != nil {
fmt.Println("Error!")
fmt.Println(err)
}
ts := []TubeStatuses{}
jsonErr := json.Unmarshal(rawData, &ts)
if jsonErr != nil {
fmt.Println(jsonErr)
}
//test
fmt.Println(ts[0].Name)
fmt.Println("Welcome to the TfL Underground checker!\nPlease enter a number for the line you want to check!\n0 - Bakerloo\n1 - central\n2 - circle\n3 - district\n4 - hammersmith & City\n5 - jubilee\n6 - metropolitan\n7 - northern\n8 - piccadilly\n9 - victoria\n10 - waterloo & city")
}
Output...
You need an array of that struct:
var tubeStatuses []struct {...}
Also, your struct member names are not exported, so they will not be unmarshaled even after you do this. Capitalize the names.
Your struct is large, so consider making it a type.
I want to create a json in golang for which I need to first create map of the following:
{"inputs": [{"data": {"image": {"url": "SOME_URL"}}}]}
how to create this map on golang. (for now even hardcoded will also work for me)
In a struct:
type SomeData struct {
Inputs []struct {
Data struct {
Image struct {
URL string `json:"url"`
} `json:"image"`
} `json:"data"`
} `json:"inputs"`
}
But if we wanted to be able to add things individually, AND be more idiomatic, we would do it like this:
type Image struct {
URL string `json:"url"`
}
type Data struct {
Image Image `json:"image"`
}
type Input struct {
Data Data `json:"data"`
}
type SomeData struct {
Inputs []Input `json:"inputs"`
}
Then, of course, we could always just use a map:
someData := map[interface{}]interface{}{}
It really just depends on which route you'd like to go. I suggest the second one as it gives you better fine-grained tooling without any pesky dirty tricks or code-clutter.
Hope this helps!
neededMap := make(map[string][]map[string]map[string]map[string]string)
When I try to do this in Solidity, it gives me
UnimplementedFeatureError: Nested dynamic arrays not implemented here.
I see this in code examples. Does Solidity not support this?
Edit - posted complete code below. So I figured out the problem is only in the last function. It doesn't like that I am returning a dynamic array. How could I implement the same functionality (I want to return a string array of data)?
pragma solidity ^0.4.6;
contract EmailIntegrity {
// Map an array of EmailIntegrityStructs for each email address.
// The first element will be used for the integrity record.
// The rest will be used for audit records.
enum ItemType { Integrity, Audit }
struct EmailIntegrityStruct {
ItemType itemType;
uint timestamp;
string data;
}
mapping(address => EmailIntegrityStruct[]) emailIntegrityStructs;
function hasEmailIntegrityData(address emailAddress) public constant returns(bool isEmail) {
return emailIntegrityStructs[emailAddress][0].timestamp == 0;
}
function insertIntegrityData(address emailAddress, uint timestamp, string data) public returns(bool success) {
if (hasEmailIntegrityData(emailAddress)) {
revert();
}
EmailIntegrityStruct memory integrityData;
integrityData.itemType = ItemType.Integrity;
integrityData.timestamp = timestamp;
integrityData.data = data;
emailIntegrityStructs[emailAddress].push(integrityData);
return emailIntegrityStructs[emailAddress].length == 1;
}
function insertAuditData(address emailAddress, uint timestamp, string data) public returns(bool success) {
if (!hasEmailIntegrityData(emailAddress)) {
revert();
}
EmailIntegrityStruct memory auditData;
auditData.itemType = ItemType.Audit;
auditData.timestamp = timestamp;
auditData.data = data;
emailIntegrityStructs[emailAddress].push(auditData);
return emailIntegrityStructs[emailAddress].length > 1;
}
function getIntegrityData(address emailAddress) public constant returns(string data) {
if (!hasEmailIntegrityData(emailAddress)) {
revert();
}
return emailIntegrityStructs[emailAddress][0].data;
}
function getAuditData(address emailAddress) public constant returns(string[] data) {
if (!hasEmailIntegrityData(emailAddress)) {
revert();
}
var length = emailIntegrityStructs[emailAddress].length;
string[] memory auditData = new string[](length - 1);
for (uint i = 1; i < length ; i++) {
auditData[i] = emailIntegrityStructs[emailAddress][i].data;
}
return auditData;
}
}
Solidity and Javascript allow nested arrays but we do not have the ability to pull a nested dynamic array from the solidity over to javascript world.
it is the limitation of the bridge between solidity and javascript. Since strings inside of solidity are represented as dynamic arrays, we cannot transfer array of strings.
UPDATE
Since then, solidity has evolved, you can return dynamic array including structs. A test contract:
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
contract Test {
struct User{
string name;
string LastName;
}
User[] public store;
function addToStorage() external returns (User[] memory ) {
User memory newUser=User(
"yilmads","bin"
);
store.push(newUser);
return store;
}
}
This compiled fine for me.
I added a definition of MyStruct and upped the Solidity version to the current one (though it compiled fine with the old pragma too). Here's the code I compiled via Remix:
pragma solidity ^0.4.19;
contract MyContract{
struct MyStruct {
uint256 foo;
}
mapping(address => MyStruct[]) myStruct;
}
Could it be that you're using an older version of the Solidity compiler? (The ^0.4.6 made me think that perhaps you are.)
What's your MyStruct? Perhaps a more interesting structure there would fail. In general, please try to provide full code samples that reproduce the problem you're seeing.
I need to initialize the following data structure which will store a json. The Attack_plans will hold multiple plans and if I loop through the GeneratePlan struct, I need all the plans that were stored.
type GeneratePlan struct {
Mode string `json:"mode"`
Name string `json:"name"`
Schema string `json:"schema"`
Version string `json:"version"`
Attack_plans []struct {
Attack_plan *Attack_plan `json:"attack-plan"`
} `json:"attack-plans"`
}
type Attack_plan struct {
Attack_resources []struct {
Attack_resource *Attack_resource `json:"attack-resource"`
} `json:"attack-resources"`
}
Can anyone please suggest something? If the data structure needs to be simplified before initializing it, then please suggest that as well. I am very new to golang so please ignore the best practices to follow. Any help is appreciated. Thanks!
why don't u just json.marshal your object to a json string, you can got answer
generatePlan := GeneratePlan{
Mode: "mode",
Name: "name",
Schema: "sachema",
Version: "version",
Attack_plans: []struct {
Attack_plan *Attack_plan `json:"attack-plan"`
}{
{Attack_plan: &Attack_plan{[]struct {
Attack_resource *Attack_resource `json:"attack-resource"`
}{
{Attack_resource: new(Attack_resource)},
{Attack_resource: new(Attack_resource)},
}}},
{Attack_plan: &Attack_plan{[]struct {
Attack_resource *Attack_resource `json:"attack-resource"`
}{
{Attack_resource: new(Attack_resource)},
{Attack_resource: new(Attack_resource)},
}}},
},
}
I found the solution! This simplifies the above data structure!
type GeneratePlan struct{
Mode string `json:"mode"`
Name string `json:"name"`
Schema string `json:"schema"`
Version string `json:"version"`
Attack_plans []struct1 `json:"attack-plans"`
}
type struct1 struct {
Attack_plan Attack_plan `json:"attack-plan"`
}
type Attack_plan struct{
Attack_resouces []struct2 `json:"attack-resources"`
}
type struct2 struct {
Attack_resource Attack_resource `json:"attack-resource"`
}
I want to use Enum values in my Apex code as we have some strict types when working with an external service, however when I get a response from the external service I'm struggling to convert the String Representation of the Enum value back to the Enum so it can be used later in my code.
To do this in C# I'd do this:
DayOfWeek wednesday =
(DayOfWeek)Enum.Parse(typeof(DayOfWeek), "Wednesday");
but in Apex code I can't find a way to do this. Does anybody have a solution?
This isn't generic, but it would work:
String dayOfWeekNameToMatch = 'Wednesday';
DayOfWeek dayOfWeekMatch;
for (DayOfWeek dow: DayOfWeek.values()) {
if (dow.name() == dayOfWeekNameToMatch) {
dayOfWeekMatch = dow;
break;
}
}
The "generic" way to do this for any enum:
public static Object parseEnum(string enumString, Type enumType) {
Type cType = Type.forName(String.format('List<{0}>', new List<String>{ enumType.getName() }));
return ((List<Object>) JSON.deserialize(String.format('["{0}"]', new List<String>{ enumString }), cType))[0];
}
Calling it is a bit awkward, but still better than other solutions (IMO):
TriggerOperation operationType = (TriggerOperation)
parseEnum('before_delete', TriggerOperation.class);
System.debug(operationType); // -> BEFORE_DELETE
Gotta love apex :)
If you have several String values for cast to enum and wouldn't like to iterate over the enum each time, you can define special map with lazy initialization:
public static Map<String, DayOfWeek> dayOfWeekByNames {
get {
if (dayOfWeekByNames == null) {
dayOfWeekByNames = new Map<String, DayOfWeek>();
for (DayOfWeek dayOfWeek : DayOfWeek.values()) {
dayOfWeekByNames.put(dayOfWeek.name(), dayOfWeek);
}
}
return dayOfWeekByNames;
}
private set;
}
Next just use this map for casting:
DayOfWeek dayOfWeek = dayOfWeekByNames.get('Monday');