diff --git a/xsdgen/cli.go b/xsdgen/cli.go
index 0274086c..1540688c 100644
--- a/xsdgen/cli.go
+++ b/xsdgen/cli.go
@@ -55,6 +55,9 @@ func (cfg *Config) GenCode(data ...[]byte) (*Code, error) {
// associated methods based on a set of XML schema.
func (cfg *Config) GenAST(files ...string) (*ast.File, error) {
data, err := cfg.readFiles(files...)
+ if err != nil {
+ return nil, err
+ }
code, err := cfg.GenCode(data...)
if err != nil {
return nil, err
@@ -62,7 +65,7 @@ func (cfg *Config) GenAST(files ...string) (*ast.File, error) {
return code.GenAST()
}
-func (cfg *Config) readFiles(files ...string) ([][]byte,error) {
+func (cfg *Config) readFiles(files ...string) ([][]byte, error) {
data := make([][]byte, 0, len(files))
for _, filename := range files {
b, err := ioutil.ReadFile(filename)
@@ -83,10 +86,8 @@ func (cfg *Config) readFiles(files ...string) ([][]byte,error) {
if err != nil {
return nil, fmt.Errorf("error reading imported files: %v", err)
}
- for _, d := range referencedData {
- // prepend imported refs (i.e. append before the referencing file)
- data = append(data, d)
- }
+ // prepend imported refs (i.e. append before the referencing file)
+ data = append(data, referencedData...)
}
data = append(data, b)
}
diff --git a/xsdgen/testdata/base64.go.golden b/xsdgen/testdata/base64.go.golden
new file mode 100644
index 00000000..0baed34e
--- /dev/null
+++ b/xsdgen/testdata/base64.go.golden
@@ -0,0 +1,214 @@
+// Code generated by xsdgen.test. DO NOT EDIT.
+
+package ws
+
+import (
+ "bytes"
+ "encoding/base64"
+ "encoding/xml"
+ "time"
+)
+
+type MyType1 []byte
+
+func (t *MyType1) UnmarshalText(text []byte) error {
+ return (*xsdBase64Binary)(t).UnmarshalText(text)
+}
+func (t MyType1) MarshalText() ([]byte, error) {
+ return xsdBase64Binary(t).MarshalText()
+}
+
+type MyType2 struct {
+ Value []byte `xml:",chardata"`
+ Length int `xml:"length,attr,omitempty"`
+}
+
+func (t *MyType2) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ type T MyType2
+ var layout struct {
+ *T
+ Value *xsdBase64Binary `xml:",chardata"`
+ }
+ layout.T = (*T)(t)
+ layout.Value = (*xsdBase64Binary)(&layout.T.Value)
+ return e.EncodeElement(layout, start)
+}
+func (t *MyType2) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
+ type T MyType2
+ var overlay struct {
+ *T
+ Value *xsdBase64Binary `xml:",chardata"`
+ }
+ overlay.T = (*T)(t)
+ overlay.Value = (*xsdBase64Binary)(&overlay.T.Value)
+ return d.DecodeElement(&overlay, &start)
+}
+
+type MyType3 struct {
+ Value time.Time `xml:",chardata"`
+ Length int `xml:"length,attr,omitempty"`
+}
+
+func (t *MyType3) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ type T MyType3
+ var layout struct {
+ *T
+ Value *xsdDate `xml:",chardata"`
+ }
+ layout.T = (*T)(t)
+ layout.Value = (*xsdDate)(&layout.T.Value)
+ return e.EncodeElement(layout, start)
+}
+func (t *MyType3) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
+ type T MyType3
+ var overlay struct {
+ *T
+ Value *xsdDate `xml:",chardata"`
+ }
+ overlay.T = (*T)(t)
+ overlay.Value = (*xsdDate)(&overlay.T.Value)
+ return d.DecodeElement(&overlay, &start)
+}
+
+type MyType4 struct {
+ Title string `xml:"http://example.org/ title"`
+ Blob []byte `xml:"http://example.org/ blob"`
+ Timestamp time.Time `xml:"http://example.org/ timestamp"`
+}
+
+func (t *MyType4) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ type T MyType4
+ var layout struct {
+ *T
+ Blob *xsdBase64Binary `xml:"http://example.org/ blob"`
+ Timestamp *xsdDateTime `xml:"http://example.org/ timestamp"`
+ }
+ layout.T = (*T)(t)
+ layout.Blob = (*xsdBase64Binary)(&layout.T.Blob)
+ layout.Timestamp = (*xsdDateTime)(&layout.T.Timestamp)
+ return e.EncodeElement(layout, start)
+}
+func (t *MyType4) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
+ type T MyType4
+ var overlay struct {
+ *T
+ Blob *xsdBase64Binary `xml:"http://example.org/ blob"`
+ Timestamp *xsdDateTime `xml:"http://example.org/ timestamp"`
+ }
+ overlay.T = (*T)(t)
+ overlay.Blob = (*xsdBase64Binary)(&overlay.T.Blob)
+ overlay.Timestamp = (*xsdDateTime)(&overlay.T.Timestamp)
+ return d.DecodeElement(&overlay, &start)
+}
+
+type MyType5 time.Time
+
+func (t *MyType5) UnmarshalText(text []byte) error {
+ return (*xsdGDay)(t).UnmarshalText(text)
+}
+func (t MyType5) MarshalText() ([]byte, error) {
+ return xsdGDay(t).MarshalText()
+}
+
+type xsdBase64Binary []byte
+
+func (b *xsdBase64Binary) UnmarshalText(text []byte) (err error) {
+ *b, err = base64.StdEncoding.DecodeString(string(text))
+ return
+}
+func (b xsdBase64Binary) MarshalText() ([]byte, error) {
+ var buf bytes.Buffer
+ enc := base64.NewEncoder(base64.StdEncoding, &buf)
+ enc.Write([]byte(b))
+ enc.Close()
+ return buf.Bytes(), nil
+}
+
+type xsdDate time.Time
+
+func (t *xsdDate) UnmarshalText(text []byte) error {
+ return _unmarshalTime(text, (*time.Time)(t), "2006-01-02")
+}
+func (t xsdDate) MarshalText() ([]byte, error) {
+ return _marshalTime((time.Time)(t), "2006-01-02")
+}
+func (t xsdDate) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ if (time.Time)(t).IsZero() {
+ return nil
+ }
+ m, err := t.MarshalText()
+ if err != nil {
+ return err
+ }
+ return e.EncodeElement(m, start)
+}
+func (t xsdDate) MarshalXMLAttr(name xml.Name) (xml.Attr, error) {
+ if (time.Time)(t).IsZero() {
+ return xml.Attr{}, nil
+ }
+ m, err := t.MarshalText()
+ return xml.Attr{Name: name, Value: string(m)}, err
+}
+func _unmarshalTime(text []byte, t *time.Time, format string) (err error) {
+ s := string(bytes.TrimSpace(text))
+ *t, err = time.Parse(format, s)
+ if _, ok := err.(*time.ParseError); ok {
+ *t, err = time.Parse(format+"Z07:00", s)
+ }
+ return err
+}
+func _marshalTime(t time.Time, format string) ([]byte, error) {
+ return []byte(t.Format(format + "Z07:00")), nil
+}
+
+type xsdDateTime time.Time
+
+func (t *xsdDateTime) UnmarshalText(text []byte) error {
+ return _unmarshalTime(text, (*time.Time)(t), "2006-01-02T15:04:05.999999999")
+}
+func (t xsdDateTime) MarshalText() ([]byte, error) {
+ return _marshalTime((time.Time)(t), "2006-01-02T15:04:05.999999999")
+}
+func (t xsdDateTime) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ if (time.Time)(t).IsZero() {
+ return nil
+ }
+ m, err := t.MarshalText()
+ if err != nil {
+ return err
+ }
+ return e.EncodeElement(m, start)
+}
+func (t xsdDateTime) MarshalXMLAttr(name xml.Name) (xml.Attr, error) {
+ if (time.Time)(t).IsZero() {
+ return xml.Attr{}, nil
+ }
+ m, err := t.MarshalText()
+ return xml.Attr{Name: name, Value: string(m)}, err
+}
+
+type xsdGDay time.Time
+
+func (t *xsdGDay) UnmarshalText(text []byte) error {
+ return _unmarshalTime(text, (*time.Time)(t), "---02")
+}
+func (t xsdGDay) MarshalText() ([]byte, error) {
+ return _marshalTime((time.Time)(t), "---02")
+}
+func (t xsdGDay) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ if (time.Time)(t).IsZero() {
+ return nil
+ }
+ m, err := t.MarshalText()
+ if err != nil {
+ return err
+ }
+ return e.EncodeElement(m, start)
+}
+func (t xsdGDay) MarshalXMLAttr(name xml.Name) (xml.Attr, error) {
+ if (time.Time)(t).IsZero() {
+ return xml.Attr{}, nil
+ }
+ m, err := t.MarshalText()
+ return xml.Attr{Name: name, Value: string(m)}, err
+}
diff --git a/xsdgen/testdata/enumeration.go.golden b/xsdgen/testdata/enumeration.go.golden
new file mode 100644
index 00000000..1970b237
--- /dev/null
+++ b/xsdgen/testdata/enumeration.go.golden
@@ -0,0 +1,34 @@
+// Code generated by xsdgen.test. DO NOT EDIT.
+
+package ws
+
+// May be one of MON, TUE, WED, THU, FRI, SAT, SUN
+type DType string
+
+const (
+ DType_MON DType = "MON"
+ DType_TUE DType = "TUE"
+ DType_WED DType = "WED"
+ DType_THU DType = "THU"
+ DType_FRI DType = "FRI"
+ DType_SAT DType = "SAT"
+ DType_SUN DType = "SUN"
+)
+
+// May be one of 0.9, 0.333333333, 0.0
+type FloatType float64
+
+const (
+ FloatType_0_9 FloatType = 0.9
+ FloatType_0_333333333 FloatType = 0.333333333
+ FloatType_0_0 FloatType = 0.0
+)
+
+// May be one of 1, 2, 3
+type IntType int
+
+const (
+ IntType_1 IntType = 1
+ IntType_2 IntType = 2
+ IntType_3 IntType = 3
+)
diff --git a/xsdgen/testdata/enumeration.xsd b/xsdgen/testdata/enumeration.xsd
new file mode 100644
index 00000000..5d8df299
--- /dev/null
+++ b/xsdgen/testdata/enumeration.xsd
@@ -0,0 +1,42 @@
+
+
+
+
+
+ type for weekday
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ integer enum
+
+
+
+
+
+
+
+
+
+
+ float enum
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/xsdgen/testdata/library.go.golden b/xsdgen/testdata/library.go.golden
new file mode 100644
index 00000000..4b12bf66
--- /dev/null
+++ b/xsdgen/testdata/library.go.golden
@@ -0,0 +1,98 @@
+// Code generated by xsdgen.test. DO NOT EDIT.
+
+package ws
+
+import (
+ "bytes"
+ "encoding/xml"
+ "time"
+)
+
+type Authors struct {
+ Person []string `xml:"http://dyomedea.com/ns/library person"`
+}
+
+type BookType struct {
+ Isbn string `xml:"http://dyomedea.com/ns/library isbn"`
+ Title string `xml:"http://dyomedea.com/ns/library title"`
+ Authors Authors `xml:"http://dyomedea.com/ns/library authors"`
+ Characters Characters `xml:"http://dyomedea.com/ns/library characters"`
+ Available string `xml:"available,attr"`
+}
+
+type Characters struct {
+ Person []string `xml:"http://dyomedea.com/ns/library person"`
+}
+
+type Library struct {
+ Book BookType `xml:"http://dyomedea.com/ns/library book"`
+}
+
+type Person struct {
+ Name string `xml:"http://dyomedea.com/ns/library name"`
+ Born time.Time `xml:"http://dyomedea.com/ns/library born"`
+ Dead time.Time `xml:"http://dyomedea.com/ns/library dead,omitempty"`
+ Qualification string `xml:"http://dyomedea.com/ns/library qualification,omitempty"`
+}
+
+func (t *Person) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ type T Person
+ var layout struct {
+ *T
+ Born *xsdDate `xml:"http://dyomedea.com/ns/library born"`
+ Dead *xsdDate `xml:"http://dyomedea.com/ns/library dead,omitempty"`
+ }
+ layout.T = (*T)(t)
+ layout.Born = (*xsdDate)(&layout.T.Born)
+ layout.Dead = (*xsdDate)(&layout.T.Dead)
+ return e.EncodeElement(layout, start)
+}
+func (t *Person) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
+ type T Person
+ var overlay struct {
+ *T
+ Born *xsdDate `xml:"http://dyomedea.com/ns/library born"`
+ Dead *xsdDate `xml:"http://dyomedea.com/ns/library dead,omitempty"`
+ }
+ overlay.T = (*T)(t)
+ overlay.Born = (*xsdDate)(&overlay.T.Born)
+ overlay.Dead = (*xsdDate)(&overlay.T.Dead)
+ return d.DecodeElement(&overlay, &start)
+}
+
+type xsdDate time.Time
+
+func (t *xsdDate) UnmarshalText(text []byte) error {
+ return _unmarshalTime(text, (*time.Time)(t), "2006-01-02")
+}
+func (t xsdDate) MarshalText() ([]byte, error) {
+ return _marshalTime((time.Time)(t), "2006-01-02")
+}
+func (t xsdDate) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ if (time.Time)(t).IsZero() {
+ return nil
+ }
+ m, err := t.MarshalText()
+ if err != nil {
+ return err
+ }
+ return e.EncodeElement(m, start)
+}
+func (t xsdDate) MarshalXMLAttr(name xml.Name) (xml.Attr, error) {
+ if (time.Time)(t).IsZero() {
+ return xml.Attr{}, nil
+ }
+ m, err := t.MarshalText()
+ return xml.Attr{Name: name, Value: string(m)}, err
+}
+func _unmarshalTime(text []byte, t *time.Time, format string) (err error) {
+ s := string(bytes.TrimSpace(text))
+ *t, err = time.Parse(format, s)
+ if _, ok := err.(*time.ParseError); ok {
+ *t, err = time.Parse(format+"Z07:00", s)
+ }
+ return err
+}
+func _marshalTime(t time.Time, format string) ([]byte, error) {
+ return []byte(t.Format(format + "Z07:00")), nil
+}
diff --git a/xsdgen/testdata/mixed-complex.go.golden b/xsdgen/testdata/mixed-complex.go.golden
new file mode 100644
index 00000000..8f517156
--- /dev/null
+++ b/xsdgen/testdata/mixed-complex.go.golden
@@ -0,0 +1,13 @@
+// Code generated by xsdgen.test. DO NOT EDIT.
+
+package ws
+
+type Number struct {
+ Value float64 `xml:",chardata"`
+ Precision int `xml:"precision,attr,omitempty"`
+}
+
+type PositiveNumber struct {
+ Value float64 `xml:",chardata"`
+ Precision int `xml:"precision,attr,omitempty"`
+}
diff --git a/xsdgen/testdata/po1.go.golden b/xsdgen/testdata/po1.go.golden
new file mode 100644
index 00000000..a613ebfc
--- /dev/null
+++ b/xsdgen/testdata/po1.go.golden
@@ -0,0 +1,14 @@
+// Code generated by xsdgen.test. DO NOT EDIT.
+
+package ws
+
+type PurchaseOrderType struct {
+ ShipTo USAddress `xml:"http://www.example.com/PO1 shipTo"`
+ BillTo USAddress `xml:"http://www.example.com/PO1 billTo"`
+ Comment string `xml:"http://www.example.com/PO1 comment,omitempty"`
+}
+
+type USAddress struct {
+ Name string `xml:"http://www.example.com/PO1 name"`
+ Street string `xml:"http://www.example.com/PO1 street"`
+}
diff --git a/xsdgen/testdata/sdn.go.golden b/xsdgen/testdata/sdn.go.golden
new file mode 100644
index 00000000..872cea35
--- /dev/null
+++ b/xsdgen/testdata/sdn.go.golden
@@ -0,0 +1,124 @@
+// Code generated by xsdgen.test. DO NOT EDIT.
+
+package ws
+
+type Address struct {
+ Uid int `xml:"http://tempuri.org/sdnList.xsd uid"`
+ Address1 string `xml:"http://tempuri.org/sdnList.xsd address1,omitempty"`
+ Address2 string `xml:"http://tempuri.org/sdnList.xsd address2,omitempty"`
+ Address3 string `xml:"http://tempuri.org/sdnList.xsd address3,omitempty"`
+ City string `xml:"http://tempuri.org/sdnList.xsd city,omitempty"`
+ StateOrProvince string `xml:"http://tempuri.org/sdnList.xsd stateOrProvince,omitempty"`
+ PostalCode string `xml:"http://tempuri.org/sdnList.xsd postalCode,omitempty"`
+ Country string `xml:"http://tempuri.org/sdnList.xsd country,omitempty"`
+}
+
+type AddressList struct {
+ Address []Address `xml:"http://tempuri.org/sdnList.xsd address,omitempty"`
+}
+
+type Aka struct {
+ Uid int `xml:"http://tempuri.org/sdnList.xsd uid"`
+ Type string `xml:"http://tempuri.org/sdnList.xsd type"`
+ Category string `xml:"http://tempuri.org/sdnList.xsd category"`
+ LastName string `xml:"http://tempuri.org/sdnList.xsd lastName,omitempty"`
+ FirstName string `xml:"http://tempuri.org/sdnList.xsd firstName,omitempty"`
+}
+
+type AkaList struct {
+ Aka []Aka `xml:"http://tempuri.org/sdnList.xsd aka,omitempty"`
+}
+
+type Citizenship struct {
+ Uid int `xml:"http://tempuri.org/sdnList.xsd uid"`
+ Country string `xml:"http://tempuri.org/sdnList.xsd country"`
+ MainEntry bool `xml:"http://tempuri.org/sdnList.xsd mainEntry"`
+}
+
+type CitizenshipList struct {
+ Citizenship []Citizenship `xml:"http://tempuri.org/sdnList.xsd citizenship,omitempty"`
+}
+
+type DateOfBirthItem struct {
+ Uid int `xml:"http://tempuri.org/sdnList.xsd uid"`
+ DateOfBirth string `xml:"http://tempuri.org/sdnList.xsd dateOfBirth"`
+ MainEntry bool `xml:"http://tempuri.org/sdnList.xsd mainEntry"`
+}
+
+type DateOfBirthList struct {
+ DateOfBirthItem []DateOfBirthItem `xml:"http://tempuri.org/sdnList.xsd dateOfBirthItem,omitempty"`
+}
+
+type Id struct {
+ Uid int `xml:"http://tempuri.org/sdnList.xsd uid"`
+ IdType string `xml:"http://tempuri.org/sdnList.xsd idType,omitempty"`
+ IdNumber string `xml:"http://tempuri.org/sdnList.xsd idNumber,omitempty"`
+ IdCountry string `xml:"http://tempuri.org/sdnList.xsd idCountry,omitempty"`
+ IssueDate string `xml:"http://tempuri.org/sdnList.xsd issueDate,omitempty"`
+ ExpirationDate string `xml:"http://tempuri.org/sdnList.xsd expirationDate,omitempty"`
+}
+
+type IdList struct {
+ Id []Id `xml:"http://tempuri.org/sdnList.xsd id,omitempty"`
+}
+
+type Nationality struct {
+ Uid int `xml:"http://tempuri.org/sdnList.xsd uid"`
+ Country string `xml:"http://tempuri.org/sdnList.xsd country"`
+ MainEntry bool `xml:"http://tempuri.org/sdnList.xsd mainEntry"`
+}
+
+type NationalityList struct {
+ Nationality []Nationality `xml:"http://tempuri.org/sdnList.xsd nationality,omitempty"`
+}
+
+type PlaceOfBirthItem struct {
+ Uid int `xml:"http://tempuri.org/sdnList.xsd uid"`
+ PlaceOfBirth string `xml:"http://tempuri.org/sdnList.xsd placeOfBirth"`
+ MainEntry bool `xml:"http://tempuri.org/sdnList.xsd mainEntry"`
+}
+
+type PlaceOfBirthList struct {
+ PlaceOfBirthItem []PlaceOfBirthItem `xml:"http://tempuri.org/sdnList.xsd placeOfBirthItem,omitempty"`
+}
+
+type ProgramList struct {
+ Program []string `xml:"http://tempuri.org/sdnList.xsd program,omitempty"`
+}
+
+type PublshInformation struct {
+ PublishDate string `xml:"http://tempuri.org/sdnList.xsd Publish_Date,omitempty"`
+ RecordCount int `xml:"http://tempuri.org/sdnList.xsd Record_Count,omitempty"`
+}
+
+type SdnEntry struct {
+ Uid int `xml:"http://tempuri.org/sdnList.xsd uid"`
+ FirstName string `xml:"http://tempuri.org/sdnList.xsd firstName,omitempty"`
+ LastName string `xml:"http://tempuri.org/sdnList.xsd lastName"`
+ Title string `xml:"http://tempuri.org/sdnList.xsd title,omitempty"`
+ SdnType string `xml:"http://tempuri.org/sdnList.xsd sdnType"`
+ Remarks string `xml:"http://tempuri.org/sdnList.xsd remarks,omitempty"`
+ ProgramList ProgramList `xml:"http://tempuri.org/sdnList.xsd programList"`
+ IdList IdList `xml:"http://tempuri.org/sdnList.xsd idList,omitempty"`
+ AkaList AkaList `xml:"http://tempuri.org/sdnList.xsd akaList,omitempty"`
+ AddressList AddressList `xml:"http://tempuri.org/sdnList.xsd addressList,omitempty"`
+ NationalityList NationalityList `xml:"http://tempuri.org/sdnList.xsd nationalityList,omitempty"`
+ CitizenshipList CitizenshipList `xml:"http://tempuri.org/sdnList.xsd citizenshipList,omitempty"`
+ DateOfBirthList DateOfBirthList `xml:"http://tempuri.org/sdnList.xsd dateOfBirthList,omitempty"`
+ PlaceOfBirthList PlaceOfBirthList `xml:"http://tempuri.org/sdnList.xsd placeOfBirthList,omitempty"`
+ VesselInfo VesselInfo `xml:"http://tempuri.org/sdnList.xsd vesselInfo,omitempty"`
+}
+
+type SdnList struct {
+ PublshInformation PublshInformation `xml:"http://tempuri.org/sdnList.xsd publshInformation"`
+ SdnEntry []SdnEntry `xml:"http://tempuri.org/sdnList.xsd sdnEntry"`
+}
+
+type VesselInfo struct {
+ CallSign string `xml:"http://tempuri.org/sdnList.xsd callSign,omitempty"`
+ VesselType string `xml:"http://tempuri.org/sdnList.xsd vesselType,omitempty"`
+ VesselFlag string `xml:"http://tempuri.org/sdnList.xsd vesselFlag,omitempty"`
+ VesselOwner string `xml:"http://tempuri.org/sdnList.xsd vesselOwner,omitempty"`
+ Tonnage int `xml:"http://tempuri.org/sdnList.xsd tonnage,omitempty"`
+ GrossRegisteredTonnage int `xml:"http://tempuri.org/sdnList.xsd grossRegisteredTonnage,omitempty"`
+}
diff --git a/xsdgen/testdata/simple-struct.go.golden b/xsdgen/testdata/simple-struct.go.golden
new file mode 100644
index 00000000..5be9b79a
--- /dev/null
+++ b/xsdgen/testdata/simple-struct.go.golden
@@ -0,0 +1,13 @@
+// Code generated by xsdgen.test. DO NOT EDIT.
+
+package ws
+
+type ComplexFoo struct {
+ Element1 Element1 `xml:"http://example.org/ns element1"`
+}
+
+// May be no more than 300 items long
+type Element1 string
+
+// Must be at least 1 items long
+type NonEmptyString string
diff --git a/xsdgen/testdata/simple-union.go.golden b/xsdgen/testdata/simple-union.go.golden
new file mode 100644
index 00000000..40db1a46
--- /dev/null
+++ b/xsdgen/testdata/simple-union.go.golden
@@ -0,0 +1,5 @@
+// Code generated by xsdgen.test. DO NOT EDIT.
+
+package ws
+
+type ConditionalUintType string
diff --git a/xsdgen/testdata/soap11.go.golden b/xsdgen/testdata/soap11.go.golden
new file mode 100644
index 00000000..daa57105
--- /dev/null
+++ b/xsdgen/testdata/soap11.go.golden
@@ -0,0 +1,69 @@
+// Code generated by xsdgen.test. DO NOT EDIT.
+
+package ws
+
+import (
+ "bytes"
+ "encoding/base64"
+ "encoding/xml"
+)
+
+// 'Array' is a complex type for accessors identified by position
+type Array struct {
+ Items []string `xml:",any"`
+ ArrayType string `xml:"arrayType,attr,omitempty"`
+}
+
+type Base64 []byte
+
+func (t *Base64) UnmarshalText(text []byte) error {
+ return (*xsdBase64Binary)(t).UnmarshalText(text)
+}
+func (t Base64) MarshalText() ([]byte, error) {
+ return xsdBase64Binary(t).MarshalText()
+}
+
+// Must match the pattern 0|1
+type Root bool
+
+type Struct []string
+
+func (a Struct) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ var output struct {
+ ArrayType string `xml:"http://schemas.xmlsoap.org/wsdl/ arrayType,attr"`
+ Items []string `xml:" item"`
+ }
+ output.Items = []string(a)
+ start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{"", "xmlns:ns1"}, Value: "http://www.w3.org/2001/XMLSchema"})
+ output.ArrayType = "ns1:anyType[]"
+ return e.EncodeElement(&output, start)
+}
+func (a *Struct) UnmarshalXML(d *xml.Decoder, start xml.StartElement) (err error) {
+ var tok xml.Token
+ for tok, err = d.Token(); err == nil; tok, err = d.Token() {
+ if tok, ok := tok.(xml.StartElement); ok {
+ var item string
+ if err = d.DecodeElement(&item, &tok); err == nil {
+ *a = append(*a, item)
+ }
+ }
+ if _, ok := tok.(xml.EndElement); ok {
+ break
+ }
+ }
+ return err
+}
+
+type xsdBase64Binary []byte
+
+func (b *xsdBase64Binary) UnmarshalText(text []byte) (err error) {
+ *b, err = base64.StdEncoding.DecodeString(string(text))
+ return
+}
+func (b xsdBase64Binary) MarshalText() ([]byte, error) {
+ var buf bytes.Buffer
+ enc := base64.NewEncoder(base64.StdEncoding, &buf)
+ enc.Write([]byte(b))
+ enc.Close()
+ return buf.Bytes(), nil
+}
diff --git a/xsdgen/xsdgen.go b/xsdgen/xsdgen.go
index 0604c4d4..f84c734f 100644
--- a/xsdgen/xsdgen.go
+++ b/xsdgen/xsdgen.go
@@ -7,6 +7,7 @@ import (
"go/ast"
"go/token"
"io"
+ "regexp"
"sort"
"strconv"
"strings"
@@ -34,7 +35,7 @@ type errorList []error
func (l errorList) Error() string {
var buf bytes.Buffer
for _, err := range l {
- io.WriteString(&buf, err.Error()+"\n")
+ _, _ = io.WriteString(&buf, err.Error()+"\n")
}
return buf.String()
}
@@ -82,7 +83,7 @@ type Code struct {
// DocType retrieves the complexType for the provided target
// namespace.
func (c *Code) DocType(targetNS string) (*xsd.ComplexType, bool) {
- key := xml.Name{targetNS, "_self"}
+ key := xml.Name{Space: targetNS, Local: "_self"}
doc, ok := c.types[key].(*xsd.ComplexType)
return doc, ok
}
@@ -247,6 +248,9 @@ func (code *Code) GenAST() (*ast.File, error) {
},
}
file.Decls = append(file.Decls, typeDecl)
+ if info.constants != nil {
+ file.Decls = append(file.Decls, info.constants.Decl)
+ }
for _, f := range info.methods {
file.Decls = append(file.Decls, f)
}
@@ -263,10 +267,12 @@ type spec struct {
name, doc string
expr ast.Expr
private bool
+ enumValues []string
methods []*ast.FuncDecl
xsdType xsd.Type
helperTypes []xml.Name
helperFuncs []string
+ constants *ast.DeclStmt
}
// Simplifies complex types derived from other complex types by merging
@@ -876,10 +882,11 @@ func (cfg *Config) genSimpleType(t *xsd.SimpleType) ([]spec, error) {
// the value would be too complex. Need a use case
// first.
result = append(result, spec{
- doc: t.Doc,
- name: cfg.public(t.Name),
- expr: builtinExpr(xsd.String),
- xsdType: t,
+ doc: t.Doc,
+ name: cfg.public(t.Name),
+ expr: builtinExpr(xsd.String),
+ enumValues: t.Restriction.Enum,
+ xsdType: t,
})
return result, nil
}
@@ -898,6 +905,7 @@ func (cfg *Config) genSimpleType(t *xsd.SimpleType) ([]spec, error) {
if err != nil {
return result, err
}
+ spec = cfg.addSpecConstants(t, spec)
return append(result, spec), nil
}
@@ -932,6 +940,36 @@ func (cfg *Config) addSpecMethods(s spec) (spec, error) {
return s, nil
}
+// add constants to enumerations
+func (cfg *Config) addSpecConstants(t *xsd.SimpleType, s spec) spec {
+ if len(t.Restriction.Enum) == 0 || nonTrivialBuiltin(t.Base) {
+ return s
+ }
+
+ var params []string
+ validChars := regexp.MustCompile("[^a-zA-Z0-9_]")
+ for _, option := range t.Restriction.Enum {
+ // create a const import with a sanitized identifier in the format {{.Type}}_{{.Value}} {{.Type}} = {{.Value}}
+ params = append(params, cfg.public(t.Name)+"_"+validChars.ReplaceAllString(option, "_"), cfg.public(t.Name), option)
+ }
+
+ s.constants = &ast.DeclStmt{}
+
+ switch t.Base.(xsd.Builtin) {
+ case xsd.Integer, xsd.NegativeInteger, xsd.PositiveInteger, xsd.NonNegativeInteger, xsd.NonPositiveInteger, xsd.Int, xsd.UnsignedInt, xsd.Long, xsd.UnsignedLong, xsd.Short, xsd.UnsignedShort:
+ s.constants.Decl = gen.ConstInt(params...)
+ case xsd.Float, xsd.Decimal, xsd.Double:
+ s.constants.Decl = gen.ConstFloat(params...)
+ case xsd.String:
+ s.constants.Decl = gen.ConstString(params...)
+ default:
+ cfg.logf("[WARNING] skipping const value generation for unknown enumerated type %s", t.Base)
+ s.constants = nil
+ }
+
+ return s
+}
+
// Generate a type declaration for a type, along with marshal/unmarshal
// methods.
func (cfg *Config) genSimpleListSpec(t *xsd.SimpleType) ([]spec, error) {
diff --git a/xsdgen/xsdgen_test.go b/xsdgen/xsdgen_test.go
index d206de7b..fcebbed2 100644
--- a/xsdgen/xsdgen_test.go
+++ b/xsdgen/xsdgen_test.go
@@ -1,41 +1,80 @@
package xsdgen
import (
+ "bytes"
"io/ioutil"
"os"
- "regexp"
+ "path"
"testing"
)
-type testLogger testing.T
+func TestExamples(t *testing.T) {
+ cases := []struct {
+ name string
+ sourceFiles []string
+ namespace string
+ }{
+ {
+ name: "base64 binary",
+ sourceFiles: []string{"testdata/base64.xsd"},
+ namespace: "http://example.org/",
+ },
+ {
+ name: "simple union",
+ sourceFiles: []string{"testdata/simple-union.xsd"},
+ namespace: "http://example.org/",
+ },
+ {
+ name: "enumeration constants",
+ sourceFiles: []string{"testdata/enumeration.xsd"},
+ namespace: "http://example.org/",
+ },
+ {
+ name: "library schema",
+ namespace: "http://dyomedea.com/ns/library",
+ sourceFiles: []string{"testdata/library.xsd"},
+ },
+ {
+ name: "purchas order schema",
+ namespace: "http://www.example.com/PO1",
+ sourceFiles: []string{"testdata/po1.xsd"},
+ },
+ {
+ name: "US treasure SDN",
+ namespace: "http://tempuri.org/sdnList.xsd",
+ sourceFiles: []string{"testdata/sdn.xsd"},
+ },
+ {
+ name: "SOAP",
+ namespace: "http://schemas.xmlsoap.org/soap/encoding/",
+ sourceFiles: []string{"testdata/soap11.xsd"},
+ },
+ {
+ name: "simple struct",
+ namespace: "http://example.org/ns",
+ sourceFiles: []string{"testdata/simple-struct.xsd"},
+ },
+ {
+ name: "mixed data",
+ namespace: "http://example.org",
+ sourceFiles: []string{"testdata/mixed-complex.xsd"},
+ },
+ }
-func grep(pattern, data string) bool {
- matched, err := regexp.MatchString(pattern, data)
- if err != nil {
- panic(err)
+ for _, c := range cases {
+ c := c
+ t.Run(c.name, func(t *testing.T) {
+ testGen(t, c.namespace, c.sourceFiles...)
+ })
}
- return matched
}
+type testLogger testing.T
+
func (t *testLogger) Printf(format string, v ...interface{}) {
t.Logf(format, v...)
}
-func TestLibrarySchema(t *testing.T) {
- testGen(t, "http://dyomedea.com/ns/library", "testdata/library.xsd")
-}
-func TestPurchasOrderSchema(t *testing.T) {
- testGen(t, "http://www.example.com/PO1", "testdata/po1.xsd")
-}
-func TestUSTreasureSDN(t *testing.T) {
- testGen(t, "http://tempuri.org/sdnList.xsd", "testdata/sdn.xsd")
-}
-func TestSoap(t *testing.T) {
- testGen(t, "http://schemas.xmlsoap.org/soap/encoding/", "testdata/soap11.xsd")
-}
-func TestSimpleStruct(t *testing.T) {
- testGen(t, "http://example.org/ns", "testdata/simple-struct.xsd")
-}
func testGen(t *testing.T, ns string, files ...string) string {
file, err := ioutil.TempFile("", "xsdgen")
if err != nil {
@@ -56,22 +95,24 @@ func testGen(t *testing.T, ns string, files ...string) string {
if err != nil {
t.Fatal(err)
}
- return string(data)
-}
-
-func TestMixedType(t *testing.T) {
- data := testGen(t, "http://example.org", "testdata/mixed-complex.xsd")
- if !grep(`PositiveNumber[^}]*,chardata`, data) {
- t.Errorf("type decl for PositiveNumber did not contain chardata, got \n%s", data)
+ ext := path.Ext(files[0])
+ goldenPath := files[0][0:len(files[0])-len(ext)] + ".go.golden"
+ // check if we have a golden file to compare to
+ if _, err := os.Stat(goldenPath); err == nil {
+ goldenData, err := ioutil.ReadFile(goldenPath)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !bytes.Equal(data, goldenData) {
+ t.Errorf("output does not match %s", goldenPath)
+ }
} else {
- t.Logf("got \n%s", data)
+ // create a new golden file if there is none
+ err = ioutil.WriteFile(goldenPath, data, 0644)
+ if err != nil {
+ t.Fatal(err)
+ }
}
-}
-
-func TestBase64Binary(t *testing.T) {
- t.Logf("%s\n", testGen(t, "http://example.org/", "testdata/base64.xsd"))
-}
-func TestSimpleUnion(t *testing.T) {
- t.Logf("%s\n", testGen(t, "http://example.org/", "testdata/simple-union.xsd"))
+ return string(data)
}