How do i properly get struct items from stripe subscription response object? - json

I am trying to grab some data from the struct of the stripe response objects for subscriptions. Here is link to the structure of the response object stribe subscription object
Here is what i have and what am trying to do
type SubscriptionDetails struct {
CustomerId string `json:"customer_id"`
SubscritpionId string `json:"subscritpion_id"`
StartAt time.Time `json:"start_at"`
EndAt time.Time `json:"end_at"`
Interval string `json:"interval"`
Plan string `json:"plan"`
PlanId string `json:"plan_id"`
SeatCount uint8 `json:"seat_count"`
PricePerSeat float64 `json:"price_per_seat"`
}
func CreateStripeSubscription(CustomerId string, planId string) (*SubscriptionDetails, error) {
stripe.Key = StripeKey
params := &stripe.SubscriptionParams{
Customer: stripe.String(CustomerId),
Items: []*stripe.SubscriptionItemsParams{
&stripe.SubscriptionItemsParams{
Price: stripe.String(planId),
},
},
}
result, err := sub.New(params)
if err != nil {
return nil, err
}
data := &SubscriptionDetails{}
data.CustomerId = result.Customer
data.SubscritpionId = result.ID
data.StartAt = result.CurrentPeriodStart
data.EndAt = result.CurrentPeriodEnd
data.Interval = result.Items.Data.Price.Recurring.Interval
data.Plan = result.Items.Data.Price.Nickname
data.SeatCount = result.Items.Data.Quantity
data.PricePerSeat = result.Items.Data.Price.UnitAmount
return data, nil
}
there are some items that are easy to get directly like ID field i got easily with result.ID and no complaints but for other items here are the error messages am getting
cannot use result.CurrentPeriodStart (type int64) as type time.Time in assignment
...
cannot use result.Customer (type *stripe.Customer) as type string in assignment
...
result.Items.Data.price undefined (type []*stripe.SubscriptionItem has no field or method price)
So how do i get the data for data.CustomerId and data.PricePerSeat?
UPDATE:
here is structure of the subscription object from stripe
type FilesStripeCreateSubscription struct {
ID string `json:"id"`
CancelAt interface{} `json:"cancel_at"`
CancelAtPeriodEnd bool `json:"cancel_at_period_end"`
CanceledAt interface{} `json:"canceled_at"`
CurrentPeriodEnd int64 `json:"current_period_end"`
CurrentPeriodStart int64 `json:"current_period_start"`
Customer string `json:"customer"`
Items struct {
Data []struct {
ID string `json:"id"`
BillingThresholds interface{} `json:"billing_thresholds"`
Created int64 `json:"created"`
Metadata struct {
} `json:"metadata"`
Object string `json:"object"`
Price struct {
ID string `json:"id"`
Active bool `json:"active"`
Currency string `json:"currency"`
CustomUnitAmount interface{} `json:"custom_unit_amount"`
Metadata struct {
} `json:"metadata"`
Nickname string `json:"nickname"`
Object string `json:"object"`
Product string `json:"product"`
Recurring struct {
AggregateUsage interface{} `json:"aggregate_usage"`
Interval string `json:"interval"`
IntervalCount int64 `json:"interval_count"`
UsageType string `json:"usage_type"`
} `json:"recurring"`
TaxBehavior string `json:"tax_behavior"`
TiersMode interface{} `json:"tiers_mode"`
TransformQuantity interface{} `json:"transform_quantity"`
Type string `json:"type"`
UnitAmount int64 `json:"unit_amount"`
UnitAmountDecimal int64 `json:"unit_amount_decimal,string"`
} `json:"price"`
Quantity int64 `json:"quantity"`
Subscription string `json:"subscription"`
TaxRates []interface{} `json:"tax_rates"`
} `json:"data"`
} `json:"items"`
}

Lets first go over the code which works under the hood, what is being returned and then we look at the problems one at a time.
When we call sub.New() method with params it returns Subscription type
Note: I will only show limited definition for types since adding the complete structure will make the answer big and not specific to the question context
Lets Look Subscription type
type Subscription struct {
...
// Start of the current period that the subscription has been invoiced for.
CurrentPeriodStart int64 `json:"current_period_start"`
// ID of the customer who owns the subscription.
Customer *Customer `json:"customer"`
...
// List of subscription items, each with an attached price.
Items *SubscriptionItemList `json:"items"`
...
}
Let looks over the first error
cannot use result.CurrentPeriodStart (type int64) as type time.Time in assignment
According to the Subscription type we can see CurrentPeriodStart is of type int64 while you are trying to set it to StartAt field of SubscriptionDetails type which is of type time.Time since the types are different one type cannot be assigned to other, to solve the issue we need to explicitly convert it to time.Time which can be done as follows:
data.StartAt = time.Unix(result.CurrentPeriodStart, 0)
time.Unix method creates time.Time type from the passed int64 value and return which we can assign to StartAt field
Now lets move on to the second error
cannot use result.Customer (type *stripe.Customer) as type string in assignment
As we can see from Subscription definition Customer field is of type *Customer it is not string type since you are trying to assign *Customer type to CustomerId field of string type which is not possible that causes the above error, referred data is incorrect then where is the correct data the correct data is available inside *Customer type withing ID field which can be retrieved as follows
data.CustomerId = result.Customer.ID
Lets go over the last error
result.Items.Data.price undefined (type []*stripe.SubscriptionItem has no field or method price)
Again if we look at Subscription definition we can see Items field is of type *SubscriptionItemList type and if we look at *SubscriptionItemList definition
type SubscriptionItemList struct {
APIResource
ListMeta
Data []*SubscriptionItem `json:"data"`
}
It contains a field name Data and Data is of type []*SubscriptionItem notice it is slice [] of *SubscriptionItem, which is the cause of the error since Data field is slice of *SubscriptionItem we can solve the problem as below:
data.PricePerSeat = result.Items.Data[0].price.UnitAmount
There are few more errors which can occur that I would like to point out please continue reading below to solve those issue
Now lets look at *SubscriptionItem definition
type SubscriptionItem struct {
...
Price *Price `json:"price"`
...
}
It contains Price field notice the name starts with capital letter and in shared code it is referred with small letter which can cause another issue and finally if we look at Price definition
type Price struct {
...
// The unit amount in %s to be charged, represented as a whole integer if possible. Only set if `billing_scheme=per_unit`.
UnitAmount int64 `json:"unit_amount"`
// The unit amount in %s to be charged, represented as a decimal string with at most 12 decimal places. Only set if `billing_scheme=per_unit`.
UnitAmountDecimal float64 `json:"unit_amount_decimal,string"`
...
}
It contains UnitAmount field which is what we are using, but there is a catch here UnitAmount is of type int64 but PricePerSeat is of type float64 assigning different type to each other will again cause error so either you can convert int64 to float64 or even better you can use UnitAmountDecimal field available within Price type containing the same data in float64 format which will reduce the explicit conversion that we would have to do when using UnitAmount field, so as per the explanation we get below solution
data.PricePerSeat = result.Items.Data[0].Price.UnitAmountDecimal

Looking at the 3 errors you have mentioned
cannot use result.CurrentPeriodStart (type int64) as type time.Time in assignment
Type of result.CurrentPeriodStart is int64 and you are trying to set it into a field of type time.Time, which will obviously fail.
The API is sending the time in unix format, which you need to parse to get it into time.Time. Do this for other time fields also
data.StartAt = time.Unix(result.CurrentPeriodStart, 0)
cannot use result.Customer (type *stripe.Customer) as type string in assignment
Similar issue as above, the field result.Customer is of type *stripe.Customer while you are trying to set it into a field of type string. Customer ID is a field inside the struct Customer
data.CustomerId = result.Customer.ID
result.Items.Data.price undefined (type []*stripe.SubscriptionItem has no field or method price)
stripe.SubscriptionItem struct does not have a field price. I am not sure what you want here. I suggest reading the subscriptions object documentation.

To access the price result.Items.Data[0].Price.UnitAmount
If you use a debugger, just put a breakpoint after the sub.New line and explore the content of the result variable

Related

Accessing Data in Nested []struct

I'm working on unmarshaling some nested json data that I have already written a struct for. I've used a tool that will generate a struct based off json data, but am a bit confused how to work with accessing nested json data (and fields can sometimes be emtpy).
Here is an example of struct:
type SomeJson struct {
status string `json:"status"`
message string `json:"message"`
someMoreData []struct {
constant bool `json:"constant,omitempty"`
inputs []struct {
name string `json:"name"`
type string `json:"type"`
} `json:"inputs,omitempty"`
Name string `json:"name,omitempty"`
Outputs []struct {
Name string `json:"name"`
Type string `json:"type"`
} `json:"outputs,omitempty"`
I'm able to unmarshal the json data and access the top level fields (such as status and message), but am having trouble accessing any data under the someMoreData field. I understand this field is a (I assume an unknown) map of structs, but only have experience working with basic single level json blobs.
For reference this is the code I have to unmarshal the json and am able to access the top level fields.
someData := someJson{}
json.Unmarshal(body, &someData)
So what is exactly the best to access some nested fields such as inputs.name or outputs.name?
to iterate over your particular struct you can use:
for _, md := range someData.someMoreData {
println(md.Name)
for _, out := range md.Outputs {
println(out.Name, out.Type)
}
}
to access specific field:
someData.someMoreData[0].Outputs[0].Name
Couple of things to note:
The struct definition is syntactically incorrect. There are couple of closing braces missing.
type is a keyword.
The status and message and other fields with lower case first letter fields are unexported. So, the Json parser will not throw error, but you will get zero values as output. Not sure that's what you observed.
someMoreData is an array of structs not map.

Unmarshal json array into struct array

I'm using Mysql 8. I'm also utilizing 99designs/gqlgen to autogenerate the structs based on the GraphQL schema.
I was to re-use the same structs when scanning MySql responses. And on top of that, while prototyping, I want to have some JSONs in my table.
So the struct is:
type CustomizedItemInput struct {
Sku string `json:"sku"`
Name string `json:"name"`
Skus []*CustomizedComponent `json:"skus"`
...
Since storing(providing Value()) is simpler I managed to store Skus into DB as a top-level JSON successfully. Looks like this:
[{"sku": "123", "position": "LEFT"}, {"sku": "456", "position": "RIGHT"}]
Now, how do I get this value out of DB and back into a array of pointers inside the struct without much hustle?
Of course, ideally it should be done without changing the underlying struct because it's autogenerated.
UPDATE:
Adding debugging information. I need to read a DB row into CustomizedItemView which basically mirrors CustomizedItemInput from above:
type CustomizedItemView struct {
Sku string `json:"sku"`
Name string `json:"name"`
Skus []*CustomizedComponentView `json:"skus"`
...
Of course, when I say "without hustle" I mean having the DB row extracted into a struct seamlessly. I can add map[string]interface{}{} with all bells and whistles and get the value. But I want to have it neat, like:
var storedCustItem = model.CustomizedItemView{}
err := udb.Get(&storedCustItem, database.SelectCustomizationQuery, userID, custItem.Sku, createdAt)
The error I get is:
2020/10/10 20:38:24 sql: Scan error on column index 8, name "skus": unsupported Scan, storing driver.Value type []uint8 into type *[]*model.CustomizedComponentView
(8 because I removed some fields for the example).
The main problem is that I can't create Scan() for an unnamed type. I have created wrappers for Value() because my inserts are more verbose and I do type conversion with the wrapper type in them:
type CustomizedComponentsIn []*CustomizedComponent
...
func (customizedComponents CustomizedComponentsIn) Value() (driver.Value, error)
...
tx.MustExec(database.SaveCustomizationCommand,
custItem.Sku,
custItem.Name,
model.CustomizedComponentsIn(custItem.Skus)
...
,which is Ok for inserts because there will be some values that do not belong to the input struct.
But I hoped to at least get the value scanned into a View struct automatically.
If you can change the type of the Skus field, the common approach would be to declare a slice type that implements the sql.Scanner and driver.Valuer interfaces and use that instead of the unnamed []*CustomizedComponent type.
For example:
type CustomizedItemInput struct {
Sku string `json:"sku"`
Name string `json:"name"`
Skus CustomizedComponentSlice `json:"skus"`
// ...
}
type CustomizedComponentSlice []*CustomizedComponent
// Value implements driver.Valuer interface.
func (s CustomizedComponentSlice) Value() (driver.Value, error) {
return json.Marshal(s)
}
// Scan implements sql.Scanner interface.
func (s *CustomizedComponentSlice) Scan(src interface{}) error {
var data []byte
switch v := src.(type) {
case string:
data = []byte(v)
case []byte:
data = v
default:
return nil
}
return json.Unmarshal(data, s)
}
If you can't change type of the Skus field you will have to explicitly convert the field during scanning.
For example, given the above named slice type, you could do something like this:
v := new(CustomizedItemView)
row := db.QueryRow("SELECT sku, name, skus FROM customized_item_view WHERE sku = ? LIMIT 1", sku)
err := row.Scan(
&v.Sku,
&v.Name,
// do the conversion here, and any other place where you're scanning Skus...
(*CustomizedComponentSlice)(&v.Skus),
)
if err != nil {
return err
}
fmt.Println(v.Skus) // result

Unmarshaling JSON into struct but converting values into required dtypes

I am using a JSON API for which I have to parse it into a struct. However, the API returns all values, even numbers, as strings and I need them to be in the format of numbers. So currently, I have a struct which has member fields which are all strings and after I have parsed the data, I loop through the entries to convert the values and add them to a new struct which has the specific entries in float or int values.
Is there any way to do the parsing and do type conversion in one go without having to use an intermediary struct representation from which to convert the values into the desired data types?
Example Code
str := []byte(`
{
"name": "Jim Burnham",
"age": "34",
"dob_day": "22",
"dob_month": "3",
"dob_year": "1984"
}
`)
Here is a sample JSON declaration of a response from an API. Notice how the age, day, month and year are returned as strings rather than integers. Now I declare a struct with the desired fields with JSON tags to map the values correctly:
type person struct {
Name string `json:"name"`
Age int `json:"age"`
DobDay int `json:"dob_day"`
DobMonth int `json:"dob_month"`
DobYear int `json:"dob_year"`
}
Then I declare an instance of the person struct and use the json package to unmarshal it into the instance of the struct:
var p person
_ = json.Unmarshal(str, &p)
fmt.Println(p)
But when I print out the person, the following output is generated:
{Jim Burnham 0 0 0 0}
As you can see, the string has been parsed correctly but the other integer fields remain at their default Golang initialized value. However, when I change the struct definition to :
type person struct {
Name string `json:"name"`
Age string `json:"age"`
DobDay string `json:"dob_day"`
DobMonth string `json:"dob_month"`
DobYear string `json:"dob_year"`
}
I get the following output:
{Jim Burnham 34 22 3 1984}
This means that currently, I have to define a raw struct which parses the information in the format of a string but then define another struct with the desired dtypes and reassign and convert the values separately, which produces untidy code as well. However, this is just one case but in my use case, there are likely thousands or even sometimes millions of such values and it seems to be inefficient, even for a compiled language. This is why I am asking for solutions for such a problem.
As explained well by #mkopriva at this link: https://play.golang.org/p/klHYlMQyb_V

Is it bad practice in Go to json.Decode a json object to an empty interface?

I have a fairly large nested JSON object I want to decode. I could decode this to a well defined nested struct, but an alternate solution I've seen is to just decode it to an empty interface.
Functionally, this works fine. But I'm wondering if behind the scenes I'm incurring a performance penalty (reflecting) when I decode the object from JSON and again when I later marshal it to JSON.
Thoughts? Thanks in advance.
Code:
CustomizationData interface{} `json:"customizationData" datastore:"-"`
vs.
CustomizationData struct {
Items []struct {
ID string `json:"id"`
Images []struct {
CustomizationState struct {
Areas []struct {
Height float64 `json:"height"`
ID string `json:"id"`
Left float64 `json:"left"`
Parent struct {
Height float64 `json:"height"`
Left float64 `json:"left"`
Top float64 `json:"top"`
Width float64 `json:"width"`
} `json:"parent"`
Rotation float64 `json:"rotation"`
Text string `json:"text"`
Top float64 `json:"top"`
URL string `json:"url"`
Width float64 `json:"width"`
} `json:"areas"`
BackgroundColor string `json:"backgroundColor"`
IsUserSet bool `json:"isUserSet"`
Orientation float64 `json:"orientation"`
} `json:"customizationState"`
SpaceId string `json:"spaceId"`
} `json:"images"`
ProductId float64 `json:"productId"`
Quantity float64 `json:"quantity"`
Sku string `json:"sku"`
TemplateName string `json:"templateName"`
} `json:"items"`
ShippingAddress struct {
City string `json:"city"`
CountryCode string `json:"countryCode"`
Email string `json:"email"`
FirstName string `json:"firstName"`
LastName string `json:"lastName"`
Line1 string `json:"line1"`
Phone string `json:"phone"`
PostalCode string `json:"postalCode"`
State string `json:"state"`
} `json:"shippingAddress"`
TimeStamp string `json:"timeStamp"`
} `json:"customizationData" datastore:"-"
And potentially more.
It depends entirely on what you intend on doing with the Unmarshalled data.
If you have nested objects / arrays in your json data, then you will end up with nested interfaces. That means you need to explicitly convert your interfaces to the correct type to access their data. In that case you are far better off using the struct in the second example as you will have your data more easily accessible as in myData.Items[0].CustomizationState.Areas[0].Height. Doing that with nested interface conversion is going to be a pain.
On the other hand, if you are just outputting this data, for example as a response to a webservice call, then you don't need to know the structure and can just spit it back out.
Personally, I always use the latter.
I assume you are using the awesome service at http://mervine.net/json2struct to convert your json into usable Go structs.
Here is a link showing the difference in ease of access between the two methods.
http://play.golang.org/p/OlJJPZcxT7
And for those who want to stay in-page:
var dataz = `{"foo": ["bar", "baz"], "boff": {"foo": "bar", "baz": "boff"}}`
type Dataz struct {
Foo []string `json:"foo"`
Boff struct {
Foo string `json:"foo"`
Baz string `json:"baz"`
} `json:"boff"`
}
func main() {
// Method 1
var d interface{}
json.Unmarshal([]byte(dataz), &d)
fmt.Println(d.(map[string]interface{})["foo"].([]interface{})[0])
// Method 2
var D Dataz
json.Unmarshal([]byte(dataz), &D)
fmt.Println(D.Foo[0])
}
EDIT
Edit based on comment about performance
Thankfully we can test it with built-in Go tools
> go test -bench .
testing: warning: no tests to run
PASS
BenchmarkInterface 300000 6208 ns/op
BenchmarkStruct 500000 3622 ns/op
ok parse 3.773s
It's the difference between handling unmarshalling of 276,000/sec or 161,000/sec. So this will almost certainly not be your bottleneck. :)

json: cannot unmarshal object into Go value of type

I can't decode the json code below ... any ideas why it doesn't work ? See also play.golang
package main
import (
"encoding/json"
)
type LocationReadable struct {
District string
City string
State string
}
type Locale struct {
Location string
CountryCode string
CurrencyId string
CurrencySymbol string
LocationReadable LocationReadable
}
type Media struct {
Image string
Video string
}
type Variations struct {
FixedPrice float64
Media Media
Quantity int
}
type PaymentData struct {
PaymentName string
PaymentService string
}
type Payment struct {
Online PaymentData
Offline PaymentData
}
type Shipping struct {
ShippingService string
ShippingName string
ShippingCost float64
HandlingTimeMax int
DispatchTimeMin int
DispatchTimeMax int
ShippingAdditionalCost int
}
type Item []struct {
_version string
CategoryId string
Title string
Media Media
SellerId string
Locale Locale
ListingType string
Payment Payment
StartTime string
EndTime string
Shipping Shipping
TitleSlug string
Variations Variations
_fpaiStatus string
}
func main() {
itemInfoR := `{"locale":{"location":"51.51121389999999,-0.11982439999997041","countryCode":"GB","currencyId":"GBP","currencySymbol":"£","locationReadable":{"district":"City of Westminster","city":"London","state":"Greater London"}},"_version":"serving","categoryId":["Root","Cameras \u0026 Photo","Digital Cameras"],"title":"many pictures","media":{"image":["//lh5.ggpht.com/O_o_N6CFkClY5AV0-LqntpyFjor7Of4u23ZcK7lYwc2uY1ea7GWi61VDJZCB7UCb79svkjKPHIenqwEUhjHi0jdIQnnl6z_p03yktPUB1FBHezIQ","//lh6.ggpht.com/ih3q2d7CenGLPyupH9FpfsoJQWQpw1i8wWA2Kd26bFnSF2fbnKyGU9WePIhCgEeqw5p6YMVmFi1c9oS0Ag93aF_oZ3ZiwK7fQuSYIrZ9VhgXbrTHkw","//lh6.ggpht.com/7RJRsapsnwWL3_KiLIjMz4QojDzUvsztXtvKTFvIfde_AHccDnOibAvXRN73tTB4SeHzlj8S1LWxbYwwWFGn9elfCKdSb8BUIU5QJY1LO791HutQ","//lh6.ggpht.com/qAtjgyHAB734Ox_4NC_fa-ZRqrCjCmJu0Tp8bo-HMO88duv8l4hhuv2REBkB--yneFzOL7annecVlGty-YsKouondiOFVnAZWzjpdrfsGfbL6wh2","//lh3.ggpht.com/dWUbASepwHF4lHaXIPnpv4BNm2pCml9MlJt7s86s1cpu-PsYNmS0yQmKFKTM38q_oMLW_YJMJ19civ2gVViKAGYcZylRW7jN3w77AJvhzS6JE2g","//lh6.ggpht.com/9aXLmPRVeZnxkwvNb3mWTF8kvfEY_lho_lOVVc9AbNqLb8GQmiS_XXVZ3OKqMv2pxgYSayMYPPRh6ACYyh0H8KtS8mPD6MKUkEajwxkTtp5Q4Lo","//lh3.ggpht.com/FG_QXZPHJ2tTYwI_t5Fg1KqivglVg9RlJn0JRsu9Ox8vJ7IcBirb2IV_I1LL_WVOMxfTuBBSDLMlrw9v0MCAdmnPCR29sCbRGjhm6zEfIH-3q2QSdw","//lh4.ggpht.com/Y23DqORrVkM2m55f-rq5_BBrlkvQg4uX7AsAt-ixhMobjK_SFgFaDfktgLhkNsyKwSr9HcF8iiGY3Nw0xOKXG1sn6wyAWg_qsolmKjVOrM5V5mIR","//lh6.ggpht.com/mQ62Ly-DjMKPMzU1OcSPJ7SLBqym0uBjawlkTHfmb-HOKaD56dnitk1duwPFJVdbi0GUpd63RQvr2VMpHp6S1OQ3di-hq4-JPeRoS5FJzksXSvW_","//lh3.ggpht.com/dqWjWPcNsvlR1tMC_agizX19f9MDiNGWFYTYVn4kjJxzIIkEe0mLzNcvS62zVJxAOaitT-IgaUfZ-Ze23BgzbqYY-l600i_LbVe35Uinz6sXIyoB","//lh6.ggpht.com/xhSdFc9uHgghs_6gf3seUWYM-PG2oLmjTrpF7ptEEMqaIrQIa8VPfC6tXE7f3M13eZvDXYqMW_k0AHO5vwCEPNp-iObixskd_lBaKNfz3MH3SNQ","//lh5.ggpht.com/kYeoKPoZGJCow-G1FhnD8kzVjNjbQA8-Kyj8eAh0HL-fMZX9tTeFPQikTZdSU0kks4-5Ui54cZF2CjGut9vfMJAVDKIq3T-bAQewCxvfl2120tH5zQ","//lh5.ggpht.com/4qUl3d-G9EPBzcYKrimNsWhQw7CmONV0jgfVhxFgB9mEU_QLRCyNJTWs2A3xf6wc7AUF2DXrKEkoX-SNLMZ6s-O4aXXV9WOjOPcWdAYreMRBld0E","//lh5.ggpht.com/z-0C4G6EWYkelAF1LjPfl_UQcsp92H4joIPt8NfsOl0nPJ2VpzZYahWadKqTLfl6kq3C6aDBcwfGQyMWSozYoZIAOAW0yRvZrwxia321PlsKTxbZ","//lh4.ggpht.com/U7I12JrDYmMC_pUXpw8DVBjBilU67BvbM8qT8gJE0bQfkhHo7FOdMttiz3syP5IR-LyO4J1WBlfmZjvMjRr4GIBt4o3Vqp-hKz7q2_OGwGtsN5s","//lh3.ggpht.com/fF2XWEtqG23ybhzClhC_p8gvKJalf1vg7k3H7UkuAaIVubil7EgOvJUCwAZk2KiCtlPYp1E5Ep2xaxZjJRmg5EFSEAjqlMHJS_Wd1Bcje6xre4s","//lh3.ggpht.com/jgOebMihBoIZvHE4EOklJvZ_k-9egjNIlUKfKFcLkvXJs8g2FXjPvdFUbwqGrkHrMtyis8uOvgt-E51Vm11hq4bieh7h0cegca0VI4vFtFaAemU","//lh3.ggpht.com/MOrI-zKNMNrQE_aHj5hzbojP3T0hEMJKK6K8UO3e1NBC-nkcQeIM1QnvtJdT_G-W4e7-qv4BiqwdWcNHBpZXOmmX3tcuYEV8u_ANEoa9_aUIfeyg","//lh6.ggpht.com/SyIS5sGOkTG7k_jFF14wzH9Evrblv6o4pHBI6z6X070-xhAeyut_kRO6xHtDID4KLcWFvItjQy-plPcJ6K1T9tlFOrtaryEPvuAYdMVx8e0TTw","//lh6.ggpht.com/2Pp9kLYFhDT3USwHinU5OxnzcWWOLI0nOWe29gOD5KMzyEcXoHkTN-AutJV9M8F_9eqAP379XB9O1d0BWPanhr-MguzKxfHeUvYTs6yHzDkxyfe0NA","//lh4.ggpht.com/7aofqklSkF3AMDfF19yqsA9J3EfEiKy1NdOelEGKNnW0Cv5tGEpq2PF_jZO1MVoBbrrmVVRv0Tdq7I8KyZbIlyHdbTs1jMl7dEFqVMvsPcyaORyHlQ","//lh4.ggpht.com/anYJHqkMCkuhmIHQTBspLtWcDTyx1ZRe84_q5pAgVEOVmsKkaKhS725N4YFoj2zpJrBP7iTC2vf1GUtrp6H7kkm8c1k6zkW6I_Gf5f9A3re_I8Ex","//lh3.ggpht.com/OtSw0rU-DvfoXgoWrQdkln6Kz7O14TF9qrPNJSGJnZLeDqUEctOn1DT09pdwwVpNQV-cXmVYQL-PX4XPhpZLWH1ciSkVT6WHNmTz1D9pHphBwJUv","//lh3.ggpht.com/cTCZnXPIjI-EO2bvQdLgeoSLOSlMFcv805n347Zyci9XDYUdcVDC_5H7SFVYDr4pC5HtQDYnrOHL6AinLW7hWtfSCLlvVhVUNQ-DlDn0NwZ-1iCO-g","//lh4.ggpht.com/i-mL_JcF9rwjQq6HnuKzuAHU41_UGxQ62IOPZvaDrATXaPFbhe-EbT7ZIpboyNA5PXRCsxNsZ9hu58edRvNs5ScgKN8Lg-00J2LhlwMAbdEsv7b0nw","//lh6.ggpht.com/D_YV2BG1WWwl67xNloP3sxzRkqhcVTgJi58L-A8nLrOcMR_tBqLz4fHEGQ-qiNcG_-32MNy3dlSPWrTBKzBcweJxgMnRVet5yuGfelUlwehDtXX_3w"],"video":[]},"sellerId":"mihai","listingType":"fixedPrice","payment":{"online":[{"paymentName":"PayPal","paymentService":"paypal"}],"offline":[{"paymentName":"Pay on Pick-up","paymentService":"payOnPickup"}]},"startTime":"2014-01-04T10:02:18+00:00","endTime":"2014-04-04T10:02:18+00:00","shipping":[{"shippingService":"economy","shippingName":"Economy","shippingCost":1.0,"handlingTimeMax":4,"dispatchTimeMin":1,"dispatchTimeMax":10,"shippingAdditionalCost":"2"},{"shippingService":"localPickup","shippingName":"Local Pick-Up","shippingCost":0.0,"handlingTimeMax":2,"dispatchTimeMin":0,"dispatchTimeMax":0,"shippingAdditionalCost":"0"}],"titleSlug":"many-pictures","variations":[{"fixedPrice":222999.0,"media":{"image":["//lh6.ggpht.com/ih3q2d7CenGLPyupH9FpfsoJQWQpw1i8wWA2Kd26bFnSF2fbnKyGU9WePIhCgEeqw5p6YMVmFi1c9oS0Ag93aF_oZ3ZiwK7fQuSYIrZ9VhgXbrTHkw","//lh6.ggpht.com/9aXLmPRVeZnxkwvNb3mWTF8kvfEY_lho_lOVVc9AbNqLb8GQmiS_XXVZ3OKqMv2pxgYSayMYPPRh6ACYyh0H8KtS8mPD6MKUkEajwxkTtp5Q4Lo","//lh3.ggpht.com/FG_QXZPHJ2tTYwI_t5Fg1KqivglVg9RlJn0JRsu9Ox8vJ7IcBirb2IV_I1LL_WVOMxfTuBBSDLMlrw9v0MCAdmnPCR29sCbRGjhm6zEfIH-3q2QSdw"],"video":[]},"quantity":1121,"Brand":"Bell \u0026 Howell"},{"fixedPrice":211.0,"media":{"image":["//lh6.ggpht.com/qAtjgyHAB734Ox_4NC_fa-ZRqrCjCmJu0Tp8bo-HMO88duv8l4hhuv2REBkB--yneFzOL7annecVlGty-YsKouondiOFVnAZWzjpdrfsGfbL6wh2","//lh3.ggpht.com/FG_QXZPHJ2tTYwI_t5Fg1KqivglVg9RlJn0JRsu9Ox8vJ7IcBirb2IV_I1LL_WVOMxfTuBBSDLMlrw9v0MCAdmnPCR29sCbRGjhm6zEfIH-3q2QSdw","//lh6.ggpht.com/9aXLmPRVeZnxkwvNb3mWTF8kvfEY_lho_lOVVc9AbNqLb8GQmiS_XXVZ3OKqMv2pxgYSayMYPPRh6ACYyh0H8KtS8mPD6MKUkEajwxkTtp5Q4Lo","//lh3.ggpht.com/MOrI-zKNMNrQE_aHj5hzbojP3T0hEMJKK6K8UO3e1NBC-nkcQeIM1QnvtJdT_G-W4e7-qv4BiqwdWcNHBpZXOmmX3tcuYEV8u_ANEoa9_aUIfeyg"],"video":[]},"quantity":2,"Brand":"Fujifilm"},{"fixedPrice":22.0,"media":{"image":["//lh3.ggpht.com/jgOebMihBoIZvHE4EOklJvZ_k-9egjNIlUKfKFcLkvXJs8g2FXjPvdFUbwqGrkHrMtyis8uOvgt-E51Vm11hq4bieh7h0cegca0VI4vFtFaAemU","//lh3.ggpht.com/MOrI-zKNMNrQE_aHj5hzbojP3T0hEMJKK6K8UO3e1NBC-nkcQeIM1QnvtJdT_G-W4e7-qv4BiqwdWcNHBpZXOmmX3tcuYEV8u_ANEoa9_aUIfeyg","//lh4.ggpht.com/anYJHqkMCkuhmIHQTBspLtWcDTyx1ZRe84_q5pAgVEOVmsKkaKhS725N4YFoj2zpJrBP7iTC2vf1GUtrp6H7kkm8c1k6zkW6I_Gf5f9A3re_I8Ex"],"video":[]},"quantity":12,"Brand":"Gateway"}],"_fpaiStatus":"published"}`
itemInfoBytes := []byte(itemInfoR)
var ItemInfo Item
er := json.Unmarshal(itemInfoBytes, &ItemInfo)
if er != nil {
panic(er)
}
}
Here's a fixed version of it: http://play.golang.org/p/w2ZcOzGHKR
The biggest fix that was needed is when Unmarshalling an array, that property needs to be an array/slice in the struct as well.
For example:
{ "things": ["a", "b", "c"] }
Would Unmarshal into a:
type Item struct {
Things []string
}
And not into:
type Item struct {
Things string
}
The other thing to watch out for when Unmarshaling is that the types line up exactly. It will fail when Unmarshalling a JSON string representation of a number into an int or float field -- "1" needs to Unmarshal into a string, not into an int like we saw with ShippingAdditionalCost int
Determining of root cause is not an issue since Go 1.8; field name now is shown in the error message:
json: cannot unmarshal object into Go struct field Comment.author of type string
You JSON doesn't match your struct fields: E.g. "district" in JSON and "District" as the field.
Also: Your Item is a slice type but your JSON is a dict value. Do not mix this up. Slices decode from arrays.
Just in case you're looking for an answer to this from an AWS perspective, particularly if you're trying to use UnmarshalListOfMaps() on the items coming back from a DynamoDB query(), then watch out for a simple typo I made.
Given that this function UnmarshalListOfMaps() takes in...a list of maps :-) then it needs to unmarshal into a List of whatever struct you're trying to build, and not just the plain struct itself. This was throwing me off because I was trying to start by querying for something that should only return one row.
movie := Movie{}
// Run the DynamoDB query
resp, err := session.Query(input) // type QueryInput
if err != nil {
log.WithError(err).Error("Error running DynamoDB query")
}
// Unmarshal all the attribute values into a Movie struct
err = dynamodbattribute.UnmarshalListOfMaps(resp.Items, &movie); if err != nil {
log.WithError(err).Error("Error marshaling DynamoDB result into Movie")
return link, err
}
The problem is in the first line. It should be movies := []Movie{} and then the reference &movie needs to change to &movies as well. If you leave it just as it is above, then the AWS Go SDK will throw this error:
cannot unmarshal list into Go value of type (etc).
If you ever face this while typing yml file not copy-pasting, it might be a problem with your yml file - mine was wrong indent.
This happened while making pipeline on concourse.