Unmarshal dynamic XML - go

I have been using unmarshal without any problems until I came across a situation where the XML tag name is dynamic.
XML Could look like:
<unit_amount_in_cents>
<USD type="integer">4000</USD>
</unit_amount_in_cents>
<setup_fee_in_cents>
<USD type="integer">4000</USD>
</setup_fee_in_cents>
or
<unit_amount_in_cents>
<GBP type="integer">4000</GBP>
</unit_amount_in_cents>
<setup_fee_in_cents>
<GBP type="integer">4000</GBP>
</setup_fee_in_cents>
or could have both (or more)
<unit_amount_in_cents>
<USD type="integer">4000</USD>
<GBP type="integer">4000</GBP>
</unit_amount_in_cents>
<setup_fee_in_cents>
<USD type="integer">4000</USD>
<GBP type="integer">4000</USD>
</setup_fee_in_cents>
I can marshal to xml w/o problems by assigning the XML.Name.Local to what I need it to be but can't unmarshal it.
Here is what the struct looks like
type Plan struct {
XMLName xml.Name `xml:"plan"`
Name string `xml:"name,omitempty"`
PlanCode string `xml:"plan_code,omitempty"`
Description string `xml:"description,omitempty"`
SuccessUrl string `xml:"success_url,omitempty"`
CancelUrl string `xml:"cancel_url,omitempty"`
DisplayDonationAmounts bool `xml:"display_donation_amounts,omitempty"`
DisplayQuantity bool `xml:"display_quantity,omitempty"`
DisplayPhoneNumber bool `xml:"display_phone_number,omitempty"`
BypassHostedConfirmation bool `xml:"bypass_hosted_confirmation,omitempty"`
UnitName string `xml:"unit_name,omitempty"`
PaymentPageTOSLink string `xml:"payment_page_tos_link,omitempty"`
PlanIntervalLength int `xml:"plan_interval_length,omitempty"`
PlanIntervalUnit string `xml:"plan_interval_unit,omitempty"`
AccountingCode string `xml:"accounting_code,omitempty"`
CreatedAt *time.Time `xml:"created_at,omitempty"`
SetupFeeInCents CurrencyArray `xml:"setup_fee_in_cents,omitempty"`
UnitAmountInCents CurrencyArray `xml:"unit_amount_in_cents,omitempty"`
}
type CurrencyArray struct {
CurrencyList []Currency
}
func (c *CurrencyArray) AddCurrency(currency string, amount int) {
newc := Currency{Amount:fmt.Sprintf("%v",amount)}
newc.XMLName.Local = currency
c.CurrencyList = append(c.CurrencyList, newc)
}
func (c *CurrencyArray) GetCurrencyValue(currency string) (value int, e error) {
for _, v := range c.CurrencyList {
if v.XMLName.Local == currency {
value, e = strconv.Atoi(v.Amount)
return
}
}
e = errors.New(fmt.Sprintf("%s not found",currency))
return
}
type Currency struct {
XMLName xml.Name `xml:""`
Amount string `xml:",chardata"`
}

You need the tag xml:",any" on your CurrencyList field.
http://play.golang.org/p/i23w03z6R4

Related

Invalid field name in struct literal Golang

I have this struct call Status and have inside another struct call Data
package model
import "time"
type Status struct {
Data struct {
OrderId string json:"orderId"
DisputeStatus string json:"disputeStatus"
DisputeCauseCategoryName string json:"disputeCauseCategoryName"
ReverseLogisticCost string json:"ReverseLogisticCost"
} json:"data"
StatusJ string json:"status"
}
And i have this function
func (s StatusServices) UpdateStatusItem(ctx context.Context, status model.Status) (error) {
statusDetails := model.Status{
OrderID: status.Data.OrderID,
}
}
In OrderId appear the error Invalid field name, i tried with this
statusDetails := model.Status{
Data: status.Data{
OrderID: status.Data.OrderID,
},
}
but i doesn't work, what can i do
The type model.Status has a field Data with a type defined as
struct {
OrderId string json:"orderId"
DisputeStatus string json:"disputeStatus"
DisputeCauseCategoryName string json:"disputeCauseCategoryName"
ReverseLogisticCost string json:"ReverseLogisticCost"
}
To initialize the variable, you can use a composite literal, more specifically a struct literal
func main() {
s := Status{
Data: struct {
OrderId string `json:"orderId"`
DisputeStatus string `json:"disputeStatus"`
DisputeCauseCategoryName string `json:"disputeCauseCategoryName"`
ReverseLogisticCost string `json:"ReverseLogisticCost"`
}{OrderId: "123456"},
}
fmt.Println(s)
}
https://play.golang.org/p/nLhLHNNf7Jb
I do not believe you can initialize a struct with a nested struct this way.
Best is to give the nested struct its own type. OR you can start with an empty struct (eg var m model.Status) and then (in a separate line) assign to s.Data.OrderId.
I believe you can only assign to a nested struct if you define and initialize it in one go (as is often done in go tests) which is consistent with the idea that this construct is useful only for onetime off uses.
Not sure it answers your question, but you may want to modify your model a little bit from :
type Status struct {
Data struct {
OrderId string json:"orderId"
DisputeStatus string json:"disputeStatus"
DisputeCauseCategoryName string json:"disputeCauseCategoryName"
ReverseLogisticCost string json:"ReverseLogisticCost"
} json:"data"
StatusJ string json:"status"
}
To:
type Status struct {
Data Data `json:"data"`
StatusJ string `json:"status"`
}
type Data struct {
OrderID string `json:"orderId"`
DisputeStatus string `json:"disputeStatus"`
DisputeCauseCategoryName string `json:"disputeCauseCategoryName"`
ReverseLogisticCost string `json:"reverseLogisticCost"`
}
Check the capital letter; in your example you had OrderId in your Data struct but status.Data.OrderID in your function.
Also your field Status does not contain an OrderID field which you were setting in your function.
As mentionned in comment, backticks were missing around your json tags.
A proper IDE would catch those.
Change OrderID to OrderId,declare the field's type upfront as per suggestion of mkopriva:
package main
import (
"fmt"
)
type StatusData struct {
OrderId string `json:"orderId"`
DisputeStatus string `json:"disputeStatus"`
DisputeCauseCategoryName string `json:"disputeCauseCategoryName"`
ReverseLogisticCost string `json:"ReverseLogisticCost"`
}
type Status struct {
Data StatusData
StatusJ string
}
func main() {
s := Status{Data: StatusData{OrderId: "123456"}}
fmt.Println(s)
}
Output:
{{123456 } }

reusing structs in another struct in golang

I have two structs in golang as below
type Data struct {
Name string
Description string
HasMore bool
}
type DataWithItems struct {
Name string
Description string
HasMore bool
Items []Items
}
At most DataWithItems struct can be rewritten as
type DataWithItems struct {
Info Data
Items []Items
}
But the above make it difficult when decoding a json object into DataWithItems. I know this can be solved with inheritance in other programming languages but Is there a way I can solve this in Go?
You can "embed" the one struct into the other:
type Items string
type Data struct {
Name string
Description string
HasMore bool
}
type DataWithItems struct {
Data // Notice that this is just the type name
Items []Items
}
func main() {
d := DataWithItems{}
d.Data.Name = "some-name"
d.Data.Description = "some-description"
d.Data.HasMore = true
d.Items = []Items{"some-item-1", "some-item-2"}
result, err := json.Marshal(d)
if err != nil {
panic(err)
}
println(string(result))
}
this prints
{"Name":"some-name","Description":"some-description","HasMore":true,"Items":["some-item-1","some-item-2"]}
Just use one struct - DataWithItems and sometimes leave items blank

Different data structures coming from a json

My question is,
I have a JSON that returns me a list of objects.
But sometimes this list returns me a single object in case the det.
I try to use the pattern below
Det []struct {
NItem string `json:"-nItem"`
Prod struct {
CProd string `json:"cProd"`
CEST string `json:"CEST"`
Cfop string `json:"CFOP"`
UCom string `json:"uCom"`
QCom string `json:"qCom"`
IndTot string `json:"indTot"`
VProd string `json:"vProd"`
CEANTrib string `json:"cEANTrib"`
UTrib string `json:"uTrib"`
NCM string `json:"NCM"`
CEAN string `json:"cEAN"`
XProd string `json:"xProd"`
VUnCom string `json:"vUnCom"`
QTrib string `json:"qTrib"`
VUnTrib string `json:"vUnTrib"`
} `json:"prod"`
Imposto struct {
VTotTrib string `json:"vTotTrib"`
ICMS struct {
ICMS00 struct {
Orig string `json:"orig"`
CST string `json:"CST"`
ModBC string `json:"modBC"`
VBC string `json:"vBC"`
PICMS string `json:"pICMS"`
VICMS string `json:" vICMS"`
} `json:"ICMS00"`
} `json:"ICMS"`
IPI struct {
CEnq string `json:"cEnq"`
IPITrib struct {
CST string `json:"CST"`
VBC string `json:"vBC"`
PIPI string `json:"pIPI"`
VIPI string `json:"vIPI"`
} `json:"IPITrib"`
} `json:"IPI"`
PIS struct {
PISAliq struct {
CST string `json:"CST"`
VBC string `json:"vBC"`
PPIS string `json:"pPIS"`
VPIS string `json:"vPIS"`
} `json:"PISAliq"`
} `json:"PIS"`
COFINS struct {
COFINSAliq struct {
CST string `json:"CST "`
VBC string `json:"vBC"`
PCOFINS string `json:"pCOFINS"`
VCOFINS string `json:"vCOFINS"`
} `json:"COFINSAliq"`
} `json:"COFINS"`
} `json:"imposto"`
} `json:"det"`
but works perfectly, but when the det object receives only element the unmarshal does not work, only works if I remove the slice of struct like that
Det struct {
NItem string `json:"-nItem"`
Prod struct {
CProd string `json:"cProd"`
CEST string `json:"CEST"`
Cfop string `json:"CFOP"`
UCom string `json:"uCom"`
QCom string `json:"qCom"`
IndTot string `json:"indTot"`
VProd string `json:"vProd"`
CEANTrib string `json:"cEANTrib"`
UTrib string `json:"uTrib"`
NCM string `json:"NCM"`
CEAN string `json:"cE
The whole structure looks like this.
SomeStruct struct {
ID string json:"-Id"
Ide struct {...} json:"ide"
Total struct {
ICMSTot struct {...} json:"ICMSTot"
} json:"total"
Cobr struct { Dup struct {....} json:"dup" } json:"cobr"
Versao string json:"-versao"
Emit struct { ... } json:"enderEmit"
Det []struct {
NItem string json:"-nItem"
Prod struct {
CProd string json:"cProd"
CEST string json:"CEST"
Cfop string json:"CFOP"
UCom string json:"uCom"
QCom string json:"qCom"
IndTot string json:"indTot"
VProd string json:"vProd"
CEANTrib string json:"cEANTrib"
UTrib string json:"uTrib"
NCM string json:"NCM"
CEAN string json:" cEAN"
XProd string json:"xProd"
VUnCom string json:"vUnCom"
QTrib string json:"qTrib"
VUnTrib string json:"vUnTrib"
} json:"prod"
Decode the JSON as a slice if the first non-whitespace byte in the input is [. Otherwise, decode the JSON as a struct.
func decode(data []byte) ([]Det, error) {
data = bytes.TrimSpace(data)
if len(data) == 0 {
return nil, errors.New("empty")
}
if data[0] == '[' {
var v []Det
err := json.Unmarshal(data, &v)
return v, err
}
var v [1]Det
err := json.Unmarshal(data, &v[0])
return v[:], err
}
Playground example
If the Det value is a member of a struct, then use RawMessage to capture the JSON and then use the function above.
type SomeStruct struct {
....
DetRaw json.RawMessage `json:"Det"`
Det []Det `json:"-"`
....
}
var v SomeStruct
if err := json.Unmarshal(data, &v); err != nil {
// handle error
}
v.Det, err = decode([]byte(v.DetRaw))
if err != nil {
// handle error
}

How to get a value from a nested Struct in Golang

Very new to statically typed languages, so I have this complex struct I'm Unmarshalling
type MyStruc1 struct {
Property1 uint16 `json:property1`
Property2 struct {
Sub2Property1 string `json:sub2property1`
Sub2Property2 uint16 `json:sub2property2`
Sub2Property3 struct {
SubSub2Property1 string `json:subsub2property1`
SubSub2Property2 string `json:subsub2property1`
} `json:sub2property3`
} `json:property2`
Property3 struct {
Sub3Property1 string `json:sub3property1`
Sub3Property2 uint16 `json:sub3property2`
Sub3Property3 struct {
SubSub3Property1 string `json:subsub3property1`
SubSub3Property2 string `json:subsub3property1`
} `json:sub3peroperty3`
} `json:property3`
Property4 string `json:property4`
}
How do i write a function or struct method to accept any one of these arrays and return the value from MyStruct1? Is this possible?
strArray1 := []string{"Property2", "Sub2Property3", "SubSub2Property1"}
strArray2 := []string{"Property3", "Sub3Property1"}
strArray3 := []string{"Property4"}
strArray4 := []string{"Property1"}
Thank you ahead of time with any replies
You can with reflect
func FieldBySlice(strct interface{}, fields []string) interface{} {
val := reflect.ValueOf(strct)
for _, field := range fields {
val = val.FieldByName(field)
}
return val.Interface()
}
This code works, but you will use it only if very much necessary. It's ugly and not recommended. Try to think different, Go is not scripting, dynamic language

Missing Xml Tag in Golang

I am new to golang I am trying to make a xml in which i am using nested tags
For that my code is
type MyXml struct {
XMLName xml.Name `xml:"myXml"`
Id int `xml:"id,attr"`
NewXml
}
type NewXml struct {
XMLName xml.Name `xml:"newXml"`
OneMoreXml
}
type OneMoreXml struct {
Msg interface{} `xml:"oneMore"`
}
type Child struct {
Param1 string `xml:"Param1"`
}
func main() {
baseXml := &Child{Param1: "Param1"}
retXml := GetXml(baseXml)
fmt.Println("my xml is", retXml)
}
func MarshallXml(reqString interface{}) (newXml string) {
xmlBody, err := xml.Marshal(reqString)
if err != nil {
fmt.Printf("error: %v\n", err)
}
newXml = string(xmlBody)
//fmt.Println(newXml)
return
}
func GetXml(baseXml interface{}) (finalXml string) {
startXml := new(MyXml)
startXml.Id = 1
startXml.Msg = baseXml
finalXml = MarshallXml(startXml)
return
}
but in my output xml Tag newXml is missing. I have tried it in various ways but some tag is always missing. I guess i am not understanding struct tag properly. So What i am doing wrong in above code and which basic concept of golang struct i am missing
I had a look at the xml package doc and they say that "an anonymous struct field is handled as if the fields of its value were part of the outer struct". In your case, all fields thus get serialized as if they were part of MyXml.
NewXml does not have any field (you just give it a name but there is nothing else) so nothing gets serialized for it.
If you add a new field to it, you can see that it is serialized.
type NewXml struct {
XMLName xml.Name `xml:"newXml"`
Test int
OneMoreXml
}
http://play.golang.org/p/vibSeQHTCr

Resources