I am consuming a REST API that returns XML and am trying to Unmarshal the XML and am having issues that appear that the omitempty is not working. Here is an example of a working XML file:
<?xml version='1.0' encoding='UTF-8'?>
<customer uri="/api/customers/339/" id="339">
<name>
<first>Firstname</first>
<last>Lastname</last>
</name>
<email>myemail#example.com</email>
<billing>
<address>
<address1>123 Main St.</address123>
<address2></address2>
<city>Nowhere</city>
<state>IA</state>
<country>USA</country>
<zip>12345</zip>
</address>
</billing>
</customer>
Here is an example of a "bad" record
<?xml version='1.0' encoding='UTF-8'?>
<customer uri="/api/customers/6848/" id="6848">
<name>
<first>Firstname</first>
<last>Lastname</last>
</name>
<email/>
<billing/>
</customer>
Now I have my structs set up like the following:
type Customer struct {
ID int `xml:"id,attr"`
Name *Name `xml:"name,omitempty"`
Billing *Billing `xml:"billing,omitempty"`
}
type Billing struct {
Address *Address `xml:"address,omitempty"`
}
type Address struct {
address_1 string `xml:",omitempty"`
address_2 string `xml:",omitempty"`
city string `xml:",omitempty"`
postal string `xml:",omitempty"`
country string `xml:",omitempty"`
}
type Name struct {
first, last string
}
Reading through all of the records it works when the XML follows the pattern of the first example <billing></billing> but when it hits a record that has something like <billing/> it throws the following error: panic: runtime error: invalid memory address or nil pointer dereference
Can someone help me figure out what's going on and how to resolve it?
You're probably misunderstanding what ,omitempty means. It takes effect when marshalling data, only. If you unmarshal <billing/> onto a pointer field with ,omitempty, it will still initialize the field. Then, since the XML element is empty, the fields of Billing itself won't be set. In practice, if you assume that customer.Billing != nil means customer.Billing.Address != nil, you'll get the observed panic.
Note: http://play.golang.org/p/dClkfOVLXh
Related
I've defined the following struct in Go:
type repoStars struct {
name string
owner string
stars int
}
And I've created an array repoItems := []repoStars{} which has multiple items of the struct above.
This is how repoItems looks like:
I'm trying to return those items as a JSON response:
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(repoItems)
And it seems empty
What am I doing wrong here?
If the struct fields start with a lower case letter it means unexported. All unexported fields won't be serialised by the encoder.
Change it to capital first letter.
type repoStars struct {
Name string
Owner string
Stars int
}
Hello I'm new at golang and programming and i have a noobish question. i couldn't find the answer on google. The soap server fails with generated code by gowsdl. but i add this xmlns="" to auth tag its works like a charm. So how can i add this to tags not by strings replace but go idiomatic way?
Not accepted by server
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
<Body xmlns="http://schemas.xmlsoap.org/soap/envelope/">
<GetCitiesRequest xmlns="http://www.n11.com/ws/schemas">
<auth> <<<<<<<<<<<<<<<<------------ fails because no xmlns=""
<appKey>xxx</appKey>
<appSecret>xx</appSecret>
</auth>
</GetCitiesRequest>
</Body>
</Envelope>
Accepted by server
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
<Body>
<GetCitiesRequest xmlns="http://www.n11.com/ws/schemas">
<auth xmlns="">
<appKey>[string]</appKey>
<appSecret>[string]</appSecret>
</auth>
</GetCitiesRequest>
</Body>
</Envelope>
im using quick fix:
buffers := new(bytes.Buffer)
buffers.WriteString(strings.ReplaceAll(buffer.String(),"<auth>","<auth xmlns=\"\">"))
req, err := http.NewRequest("POST", s.url, buffers)
what should i add struct tag to see empty xmlns="" ?
type GetCitiesRequest struct {
XMLName xml.Name `xml:"http://www.n11.com/ws/schemas GetCitiesRequest"`
Auth *Authentication `xml:"auth,omitempty" json:"auth,omitempty"`
}
type Authentication struct {
AppKey string `xml:"appKey,omitempty" json:"appKey,omitempty"`
AppSecret string `xml:"appSecret,omitempty" json:"appSecret,omitempty"`
}
Alos i tried;
type Authentication struct {
XMLName xml.Name `xml:""`
AppKey string `xml:"appKey,omitempty" json:"appKey,omitempty"`
AppSecret string `xml:"appSecret,omitempty" json:"appSecret,omitempty"`
}
auth := Authentication{AppKey:"secret",AppSecret:"secret"}
auth.XMLName.Local= "auth"
auth.XMLName.Space = ""
Also i tried auth.XMLName.Space = " " empty space but xml.marshal transform it to escaped character like ""e,#34"
I want to understand how can i do with like pro way but not noob way.
Any help appreciated.
Thank you.
another problem :/ not solved
tried xml:"* categoryId" turns out -> <categoryId xmlns="*">1001770</categoryId>
yet soap api not accepting * character wants <categoryId xmlns="">1001770</categoryId>
type Authentication struct {
Xmlns string `xml:"xmlns,attr" json:"-"`
AppKey string `xml:"appKey,omitempty" json:"appKey,omitempty"`
AppSecret string `xml:"appSecret,omitempty" json:"appSecret,omitempty"`
}
type GetSubCategoriesRequest struct {
XMLName xml.Name `xml:"http://www.n11.com/ws/schemas GetSubCategoriesRequest"`
Auth *Authentication `xml:"auth,omitempty" json:"auth,omitempty"`
CategoryId int64 `xml:"* categoryId,omitempty" json:"categoryId,omitempty"` <<<<<<<-------- i need xmlns=""
}
any help?
https://golang.org/pkg/encoding/xml/#Marshal
a field with tag "name,attr" becomes an attribute with the given name in the XML element.
a field with tag ",attr" becomes an attribute with the field name in the XML element.
type Authentication struct {
Xmlns string `xml:"xmlns,attr" json:"-"`
AppKey string `xml:"appKey,omitempty" json:"appKey,omitempty"`
AppSecret string `xml:"appSecret,omitempty" json:"appSecret,omitempty"`
}
https://play.golang.org/p/iIvlUoaYvgB
i need to save some data where it consist of a fields called ID which is an uuid im using golang and olivere elastic search package here is my code
type Space struct {
ID string `json:"id"`
Name string `json:"name"`
Type string `json:"type"`
}
After doing some conversions
js := string(data)
// upto this place there is no error or warnings
ind, err := esclient.Index().
Index(Type).
BodyJson(js).
Do(ctx)
Here is the error it throws
failed to parse field [id] of type [long] in document with id
'gPmI8HIBCIO6Ejb-Y51D'. Preview of field's value:
'a5c723c5-1f6e-457f-9556-47b7ebcfd183'
The Error message is very clear that you are trying to store a5c723c5-1f6e-457f-9556-47b7ebcfd183 which is a string and not long as id field is mapped as long in your elasticsearch mapping.
It's even also telling that you got an error for document with _id having gPmI8HIBCIO6Ejb-Y51D as value, so you can find this document and correct it.
Please send the proper value of the below field, valid long in your case in order to solve the issue.
ID string `json:"id"`
Refer this code from Elasticsearch which parse the values and throws the exception, if you want to get more code level details.
I'm new to Go and working with a set of types generated by gowsdl based on the NetSuite SuiteTalk web service definition. It has created the following types:
type BaseRef struct {
XMLName xml.Name `xml:"urn:core_2018_2.platform.webservices.netsuite.com BaseRef"`
Name string `xml:"name,omitempty"`
}
type RecordRef struct {
XMLName xml.Name `xml:"urn:core_2018_2.platform.webservices.netsuite.com RecordRef"`
*BaseRef
InternalId string `xml:"internalId,attr,omitempty"`
ExternalId string `xml:"externalId,attr,omitempty"`
Type *RecordType `xml:"type,attr,omitempty"`
}
type GetRequest struct {
XMLName xml.Name `xml:"urn:messages_2018_2.platform.webservices.netsuite.com GetRequest"`
BaseRef *BaseRef `xml:"baseRef,omitempty"`
}
As I try to use these types it is unhappy in my ability to use the specific type of reference record in a GetRequest structure which is looking for a BaseRef which is what RecordRef is based on.
var partnerRecordType RecordType
partnerRecordType = RecordTypePartner
recordRef := RecordRef{
Type:&partnerRecordType,
InternalId:internalIdString,
}
var getRequest GetRequest
getRequest.BaseRef = &recordRef
The error I get is on the last line is:
cannot use &recordRef (type *RecordRef) as type *BaseRef in assignment
Any thoughts on how to proceed?
Go does not support polymorphism in this way, neither does it support inheritance in way that say C# or Java does. Embedded structs are quite literally just embedded, they do not create a classical inheritance hierarchy. They simply give the wrapping struct all of the exposed methods and fields of the embedded struct (with some subtle caveats - check out the spec)
That said, in your example RecordRef is not related to BaseRef in terms of its type, instead it could be considered to "contain" a pointer to a BaseRef. In order for your program to compile, you will have explicitly assign the embedded BaseRef like so:
getRequest.BaseRef = &recordRef.BaseRef
As this code you are referencing has been auto generated from a WSDL, it may be a little cumbersome to update the GetRequest to provide a BaseRef like data structure in a more polymorphic, flexible fashion, but in order to do so you will need to use Go interfaces.
You could update the GetRequest to have a method with accepts in interface type, say XmlRef which would expose getters that can derive the data you need to assign to the GetRequest
For example
type XmlRef interface {
Name() string
InternalID() string
ExternalID() string
}
func (r *GetRequest) SetRef(ref XmlRef) {
r.BaseRef.Name = ref.Name()
// etc...
}
Then simply implement the interface for RecordRef and any other structs that would need to be used in this context.
If I understand you right, you are looking for a method to access a struct's embbed field. You can simply use recordRef.BaseRef for that purpose.
Further reading: https://golang.org/ref/spec#Struct_types
It is possible to connect to netsuite with the output of go2wsdl but many tweaks are required. A full proof of concept is available here:
https://github.com/mk-j/go-netsuite-soap
But here are some examples of some of the tweaks required:
sed -i 's/urn:.*1.lists.webservices.netsuite.com //' netsuite.go
Tweaking BaseRef and GetRequest:
type BaseRef struct {
Name string `xml:"name,omitempty" json:"name,omitempty"`
InternalId string `xml:"internalId,attr,omitempty" json:"internalId,omitempty"`
ExternalId string `xml:"externalId,attr,omitempty" json:"externalId,omitempty"`
Type *RecordType `xml:"type,attr,omitempty" json:"type,omitempty"`
XsiType string `xml:"xsi:type,attr,omitempty"`
}
type GetRequest struct {
XMLName xml.Name `xml:"get"`
XmlNSXSI string `xml:"xmlns:xsi,attr"`
XmlNSPC string `xml:"xmlns:platformCore,attr"`
BaseRef *BaseRef `xml:"baseRef,omitempty" json:"baseRef,omitempty"`
}
Then this code produced an successful xml response from the soap server:
recordType := netsuite.RecordTypePartner
baseRef :=netsuite.BaseRef{
InternalId:"1234",
Type:&recordType,
XsiType:"platformCore:RecordRef",
}
request := netsuite.GetRequest{
BaseRef:&baseRef,
XmlNSXSI: "http://www.w3.org/2001/XMLSchema-instance",
XmlNSPC:"urn:core_2022_1.platform.webservices.netsuite.com",
}
getResponse, err := service.Get(&request)
Which made the raw request:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header><tokenPassport>[redacted]</tokenPassport></soap:Header>
<soap:Body>
<get xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:platformCore="urn:core_2022_1.platform.webservices.netsuite.com">
<baseRef internalId="1234" type="partner" xsi:type="platformCore:RecordRef"></baseRef>
</get>
</soap:Body>
</soap:Envelope>
Sources used: https://www.zuar.com/blog/netsuite-api-exploring-soap/
I have such xml document and I need to get an array of DATA. I cannot solve this simple task for 4 hours.... want back to node.js :-)
<?xml version="1.0" standalone="no"?>
<RETS ReplyCode="0" ReplyText="Operation Successful" >
<COUNT Records="58951" />
<DELIMITER value="09"/>
<COLUMNS> LN </COLUMNS>
<DATA> 09361303 </DATA>
<DATA> 09333085 </DATA>
<MAXROWS/>
type DATA struct {
DATA string `xml:"DATA"`
}
type Rets struct {
DATA []DATA `xml:"RETS>DATA"`
}
data := &Rets{}
decoder := xml.Unmarshal(body,&data)
fmt.Println(decoder)
There are a couple of tools for turning XML into Go structs. One that is suitable for reading is zek (try it online), and although it is experimental, it can deal with a couple of common XML constructs already.
You can go from a XML file (raw) to a struct with a simple command:
$ curl -sL https://git.io/fN4Pq | zek -e -p -c
This will create a struct plus an example program for testing out the marshalling (the example does very little, it read XML from stdin and turns it into JSON).
Here is an example struct (for some XML take from this repo):
// RETS was generated 2018-09-07 12:11:10 by tir on apollo.local.
type RETS struct {
XMLName xml.Name `xml:"RETS"`
Text string `xml:",chardata"`
ReplyCode string `xml:"ReplyCode,attr"`
ReplyText string `xml:"ReplyText,attr"`
METADATATABLE struct {
Text string `xml:",chardata"`
Resource string `xml:"Resource,attr"`
Class string `xml:"Class,attr"`
Version string `xml:"Version,attr"`
Date string `xml:"Date,attr"`
COLUMNS string `xml:"COLUMNS"` // MetadataEntryID SystemNam...
DATA []string `xml:"DATA"` // 7 City City Ci ...
} `xml:"METADATA-TABLE"`
}
Disclaimer: I wrote zek.
From the xml.Unmarshal docs
If the XML element contains character data, that data is
accumulated in the first struct field that has tag ",chardata".
The struct field may have type []byte or string.
If there is no such field, the character data is discarded.
Use
type DATA struct {
DATA string `xml:",chardata"`
}
type Rets struct {
DATA []DATA `xml:"DATA"`
}
Or in this simple case, you can just use
type Rets struct {
DATA []string `xml:"DATA"`
}
Which collects the charadata from a list of elements by default