GO : Define a type which is Map with 2 specifique Key - go

I want to define a type Message which is basically a map[string]string with 2 specific keys : message and from.
A Message should be :
map[string]string{"message": "Hello", "from": "Me"}
I defined a type Message :
type Message struct {
message string,
from string
}
Note : I need to convert this Message to json in order to send it though http request, that's why I "need" to use map[string]string => I finnaly found out that it's also possible to serialized struct as JSON objects
It is possible to defined a type which is a map with specific key with go ?
What would be the idiomatic solution to do this in Go ?

As revealed in the comments on the question, the reason for preferring map[string]string is to preserve JSON encoding. However, structs can be serialized as JSON objects
import "json"
type Message struct {
Message string `json:"message"`
From string `json:"from"`
}
myMessage := Message{Message: "foo", From: "bar"};
serialized, err := json.Marshal(myMessage);
if err != nil {
// handle the error
}

Related

Dynamically Create Structs in Golang

So I am working with an external API, whose responses I wanted to parse. The incoming responses are of a fixed format i.e.
type APIResponse struct {
Items []interface{} `json:"items"`
QuotaMax int `json:"quota_max"`
QuotaRemaining int `json:"quota_remaining"`
}
So for each response I am parsing the items. Now the items can be of diff types as per the request. It can be a slice of sites, articles, etc. Which have their individual models. like:
type ArticleInfo struct {
ArticleId uint64 `json:"article_id"`
ArticleType string `json:"article_type"`
Link string `json:"link"`
Title string `json:"title"`
}
type SiteInfo struct {
Name string `json:"name"`
Slug string `json:"slug"`
SiteURL string `json:"site_url"`
}
Is there any way, when parsing the input define the type of Items in APIResponse. I don't want to create separate types for individual responses.
Basically want to Unmarshall any incoming response into the APIResponse struct.
Change type of the Items field to interface{}:
type APIResponse struct {
Items interface{} `json:"items"`
...
}
Set the response Items field to pointer of the desired type. Unmarshal to the response:
var articles []ArticleInfo
response := APIResponse{Items: &articles}
err := json.Unmarshal(data, &response)
Access the articles using variable articles.
Run an example on the playground.

Use different json keys for marshalling and unmarshalling

What do I have: two structs for some kind of API
type BaseUser struct {
ID int64 `json:"user_id"`
Name string `json:"user_name"`
Email string `json:"user_email"`
}
and
type UserWithAddress struct {
BaseUser
Postal string `json:"user_postal"`
City string `json:"user_city"`
Street string `json:"user_street"`
}
What do I want to do: convert json keys from snake_case to camelCase.
Lets say, this is a request body
{
"user_id": 123,
"user_name": "test",
"user_email": "test#mail.com",
"user_postal": "12312",
"user_city": "city",
"user_street": "street"
}
So as a result, after some kind of transformation, I'd like to have this output
{
"userId": 123,
"userName": "test",
"userEmail": "test#mail.com",
"userPostal": "12312",
"userCity": "city",
"userStreet": "street"
}
How do I handle this at the moment: I made another two structs with camelCase json tag
type BaseUserCamelCase struct {
ID int64 `json:"userId"`
Name string `json:"userName"`
Email string `json:"userEmail"`
}
and
type UserWithAddressCamelCase struct {
BaseUserCamelCase
Postal string `json:"userPostal"`
City string `json:"userCity"`
Street string `json:"userStreet"`
}
My transformation looks like
var userWithAddressCamelCase UserWithAddressCamelCase
userWithAddressCamelCase.BaseUserCamelCase = BaseUserCamelCase(userWithAddress.BaseUser)
//I can't cast whole userWithAddressCamelCase object to another type because of different field names - BaseUser and BaseUserCamelCase
userWithAddressCamelCase.Name = userWithAddress.Name
userWithAddressCamelCase.Email = userWithAddress.Email
userWithAddressCamelCase.Postal = userWithAddress.Postal
//and so on
and I don't like it, because if BaseUser or UserWithAddress will grow up, I have to add appropriate field to %CamelCase structs.
My question: is there another more efficient way to handle keys transformation?
Is there another more efficient way to handle keys transformation?
No.
(Well, based on your definition of "efficient". You could use reflection, but I will not recommend this. Your code is perfectly fine. If any struct grows you add a few lines of simple code. There is nothing wrong with simple code which is not going to produce errors and is fast during execution. Just because it doesn't look fancy it doesn't mean that there is anything to "improve" here.)
If the need to maintain the field list is what concerns you the most, then I'd suggest making type aliases for your user types and implementing the json.Marshaler interface for those aliases, where you'd implement custom JSON encoding. You can even introduce an alternative set of tags and use those there.
Something along these lines:
type BaseUser struct {
ID int64 `json:"user_id" jsonCC:"userId"`
Name string `json:"user_name" jsonCC:"userName"`
Email string `json:"user_email" jsonCC:"userEmail"`
}
type BaseUserCamelCase BaseUser
func (bucc BaseUserCamelCase) MarshalJSON() ([]byte, error) {
buccVal := reflect.ValueOf(bucc)
kvpairs := []string{}
for i := 0; i < buccVal.NumField(); i++ {
k := buccVal.Type().Field(i).Tag.Get("jsonCC")
v := buccVal.Field(i).Interface() //TODO: proper JSON encoding of things
kvpairs = append(kvpairs, fmt.Sprintf("\"%s\":%#v", k, v))
}
return []byte(fmt.Sprintf("{%s}", strings.Join(kvpairs, ","))), nil
}
Then you can choose marshaling style:
user := BaseUser{
ID: 123,
Name: "Johnny D03",
Email: "j#example.com",
}
json.Marshal(user)
// {"user_id":123,"user_name":"Johnny D03","user_email":"j#example.com"}
json.Marshal(BaseUserCamelCase(user))
// {"userId":123,"userName":"Johnny D03","userEmail":"j#example.com"}

Validate yaml schema with golang (semantic check)

We have tool which need to read YAML file with specific structure. When we got the YAML file we need to know if
Check if the YAML file is valid according to some guideline - semantic check
Where is the syntax error if any
For example this is example of the validation that we need to address
_version: {required: true}
id: {required: true, pattern: '/^[A-Za_\-\.]+$/'}
release-version: {required: true}
type:
builds:
type:seq
sequence:
-type:map
mapping:
name:{required: true, unique: true, pattern: '/^[A-Za-z0-3_\-\.]+$/'}
params:
type: map
mapping: { =: {type: any} }
Mapping is a key value object
seq can have multiple builds
type any is and key value
We use this open source to parse the yaml https://github.com/go-yaml/yaml
One idea (which is good) is to convert to json like following to do it by converting the file to json and validate it which have library to support it, any example in my context will be very helpful https://github.com/xeipuuv/gojsonschema
But not sure how I handle
Type map
Type seq
Here is what you could try.
Model a struct after the shape of the expected yaml data:
type Config struct {
Version struct {
Required bool
}
ID struct {
Required bool
Pattern string
}
ReleaseVersion struct {
Required bool
}
Type interface{}
Builds struct {
Type []interface{} `yaml:"type"`
Sequence struct {
Type string
}
Mapping struct {
Name map[string]interface{}
Params struct {
Type string `yaml:"type"`
Mapping struct {
To map[string]string `yaml:"="`
}
}
} `yaml:"mapping"`
}
}
The yaml flag yaml:"somefield" is added to label the field name of the yaml the data we're interested in.
Also many fields with unknown/undetermined type can be declared as empty interface (interface{}) or if you want to "enforce" that the underlying form is a key-value object you can either declare it as a map[string]interface{} or another struct.
We then unmarshal the yaml data into the struct:
cfg := Config{}
err := yaml.Unmarshal([]byte(data), &cfg)
if err != nil {
log.Fatalf("error: %v", err)
}
Since we have modeled fields as either anonymous structs or maps, we can test if a particular field has a "key-value" value by checking its equality to nil.
// Mapping is a key value object
if (Mapping != nil) {
// Mapping is a key-value object, since it's not nil.
}
// type any is and key value
// Mapping.To is declared with map[string]string type
// so if it's not nil we can say there's a map there.
if (Mapping.To != nil) {
// Mapping.To is a map
}
In marshaling/unmarshaling, maps and structs are pretty interchangeable. The benefit of a struct is you can predefine the field's names ahead of time while unmarshaling to a map it won't be clear to you what the keys are.
You can make go-yaml work with jsonschema. See this issue: https://github.com/santhosh-tekuri/jsonschema/issues/5
In short:
Create a custom yaml parser that produces compatible output types, as per this issue.
Parse the yaml into an interface{} using that custom parser
Validate with jsonschema.ValidateInterface.
(once yaml.v3 has been released, the custom unmarshaller should be able to be replaced with a configuration option)
I was originally using the accepted answer's approach of parsing into a struct and then writing code to manually validate that the struct met my spec. This quickly got very ugly - the above approach allows for a clean separate spec and reliable validation of it.

How to write dynamically typed data to field within a struct

I have the following response struct that I want to use as a base wrapper for responding to API calls users send to me.
type Response struct {
Data ??? `json:"data,omitempty"`
Time int64 `json:"time,omitempty"`
Message string `json:"message,omitempty"`
}
The type of the Data field is varying and could be a map[string]*CustomStruct1 map[string*CustomStruct2 or an []CustomStruct3.
What is the best way to attack this kind of problem?
One option is to simply treat "Data" as the interface{} (any) type, instead of using your custom structs, and handle the resulting values based on inspection of what actually got unmarshaled. Of course, once you've inspected the data to determine what type it should be you could convert it into the appropriate strong type after the fact.
type Response struct {
Data interface{} `json:"data,omitempty"`
Time int64 `json:"time,omitempty"`
Message string `json:"message,omitempty"`
}
Another option is to embed the "Response" struct into specialized structs that look for your custom types and unmarshal into the appropriate one, assuming you know which one you've got ahead of time:
type BaseResponse struct {
Time int64 `json:"time,omitempty"`
Message string `json:"message,omitempty"`
}
type Response1 struct {
BaseResponse
Data map[string]*CustomStruct1 `json:"data"`
}
type Response2 struct {
BaseResponse
Data map[string]*CustomStruct2 `json:"data"`
}
// etc...
Ultimately, the unmarshaler cannot pick a varying type based on the document that gets unmarshaled, it only deserializes JSON values into structures either defined explicitly by you or into generic ones.
You could try to use reflection, but it wouldn't be very idiomatic.

exported field in unexported struct

Example:
type myType struct {
foo []float64
Name string
}
myType is not exported, but Name field in it is exported.
Does this make sense to do this? Is that considered a bad practice?
I have something like this, and it compiles fine.
I can access the Name field if I create an exported array of myType:
var MyArray []myType = {... some initialization }
fmt.Println(MyArray[0].Name) // Name is visible and it compiles
It is perfectly valid to have unexported structs with exported fields. If the type is declared in another package, the declaration var MyArray []myType would be a compile-time error.
While it is perfectly valid to have an exported function with an unexported return type, it is usually annoying to use. The golint tool also gives a warning for such cases:
exported func XXX returns unexported type pname.tname, which can be annoying to use
In such cases it's better to also export the type; or if you can't or don't want to do that, then create an exported interface and the exported function should have a return type of that interface, and so the implementing type may remain unexported. Since interfaces cannot have fields (only methods), this may require you to add some getter methods.
Also note that in some cases this is exactly what you want: unexported struct with exported fields. Sometimes you want to pass the struct value to some other package for processing, and in order for the other package to be able to access the fields, they must be exported (but not the struct type itself).
Good example is when you want to generate a JSON response. You may create an unexported struct, and to be able to use the encoding/json package, the fields must be exported. For example:
type response struct {
Success bool `json:"success"`
Message string `json:"message"`
Data string `json:"data"`
}
func myHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json;charset=UTF-8")
resp := &response{
Success: true,
Message: "OK",
Data: "some data",
}
if err := json.NewEncoder(w).Encode(resp); err != nil {
// Handle err
}
}

Resources