Documentation ¶
Overview ¶
Package xsdgen generates Go source code from xml schema documents.
The xsdgen package generates type declarations and accompanying methods for marshalling and unmarshalling XML elements that adhere to an XML schema. The source code generation is configurable, and can be passed through user-defined filters.
Code generated by the xsdgen package is self-contained; only the Go standard library is used. All types generated by the xsdgen package can be unmarshalled into by the standard encoding/xml package. Where neccessary, methods are generated to satisfy the interfaces used by encoding/xml.
Index ¶
- Variables
- type Code
- type Config
- func (cfg *Config) GenAST(files ...string) (*ast.File, error)
- func (cfg *Config) GenCLI(arguments ...string) error
- func (cfg *Config) GenCode(data ...[]byte) (*Code, error)
- func (cfg *Config) GenSource(files ...string) ([]byte, error)
- func (cfg *Config) NameOf(name xml.Name) string
- func (cfg *Config) Option(opts ...Option) (previous Option)
- type Logger
- type Option
- func AllowType(name xml.Name) Option
- func ApplyXMLNameToTopLevelElementTypes(applyXMLName bool) Option
- func FollowImports(follow bool) Option
- func HandleSOAPArrayType() Option
- func IgnoreAttributes(names ...string) Option
- func IgnoreElements(names ...string) Option
- func LogLevel(level int) Option
- func LogOutput(l Logger) Option
- func Namespaces(xmlns ...string) Option
- func OnlyTypes(patterns ...string) Option
- func PackageName(name string) Option
- func ProcessTypes(fn func(xsd.Schema, xsd.Type) xsd.Type) Option
- func Replace(pat, repl string) Option
- func SOAPArrayAsSlice() Option
- func TargetNamespacesOnly(tnsOnly bool) Option
- func UseFieldNames() Option
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var DefaultOptions = []Option{ IgnoreAttributes("id", "href", "ref", "offset"), Replace(`[._ \s-]`, ""), PackageName("ws"), HandleSOAPArrayType(), SOAPArrayAsSlice(), UseFieldNames(), }
DefaultOptions are the default options for Go source code generation. The defaults are chosen to be good enough for the majority of use cases, and produce usable, idiomatic Go code. The top-level Generate function of the xsdgen package uses these options.
Functions ¶
This section is empty.
Types ¶
type Code ¶
type Code struct {
// contains filtered or unexported fields
}
Code is the intermediate representation used by the xsdgen package. It can be used to generate a Go source file, and to lookup identifiers and attributes for a given type.
func (*Code) DocType ¶
func (c *Code) DocType(targetNS string) (*xsd.ComplexType, bool)
DocType retrieves the complexType for the provided target namespace.
type Config ¶
type Config struct {
// contains filtered or unexported fields
}
A Config holds user-defined overrides and filters that are used when generating Go source code from an xsd document.
func (*Config) GenAST ¶
GenAST creates an *ast.File containing type declarations and associated methods based on a set of XML schema.
func (*Config) GenCLI ¶
GenCLI creates a file containing Go source generated from an XML Schema. Main is meant to be called as part of a command, and can be used to change the behavior of the xsdgen command in ways that its command-line arguments do not allow. The arguments are the same as those passed to the xsdgen command.
Example ¶
package main import ( "log" "github.com/henryolik/go-xml/v2/xsdgen" ) func main() { var cfg xsdgen.Config cfg.Option( xsdgen.IgnoreAttributes("id", "href", "offset"), xsdgen.IgnoreElements("comment"), xsdgen.PackageName("webapi"), xsdgen.Replace("_", ""), xsdgen.HandleSOAPArrayType(), xsdgen.SOAPArrayAsSlice(), ) if err := cfg.GenCLI("webapi.xsd", "deps/soap11.xsd"); err != nil { log.Fatal(err) } }
Output:
func (*Config) GenCode ¶
GenCode reads all xml schema definitions from the provided data. If succesful, the returned *Code value can be used to lookup identifiers and generate Go code.
func (*Config) GenSource ¶
The GenSource method converts the AST returned by GenAST to formatted Go source code.
type Logger ¶
type Logger interface {
Printf(format string, v ...interface{})
}
Types implementing the Logger interface can receive debug information from the code generation process. The Logger interface is implemented by *log.Logger.
type Option ¶
An Option is used to customize a Config.
func AllowType ¶
AllowType registers the canonical XML name of a type that should be generated by the xsdgen package. If AllowType is called at least once, only types passed to AllowType, and their dependent types, will be generated.
func ApplyXMLNameToTopLevelElementTypes ¶
ApplyXMLNameToTopLevelElementTypes specifies whether or not to apply XMLName to the structs that are generated for anonymous complexTypes inside top level elements
func FollowImports ¶
FollowImports specifies whether or not to recursively read in imported schemas before attempting to parse
func HandleSOAPArrayType ¶
func HandleSOAPArrayType() Option
The Option HandleSOAPArrayType adds a special-case pre-processing step to xsdgen that parses the wsdl:arrayType attribute of a SOAP array declaration and changes the underlying base type to match.
Example ¶
package main import ( "fmt" "log" "os" "github.com/henryolik/go-xml/v2/xsdgen" ) func tmpfile() *os.File { f, err := os.CreateTemp("", "xsdgen_test") if err != nil { panic(err) } return f } func xsdfile(s string) (filename string) { file := tmpfile() defer file.Close() fmt.Fprintf(file, ` <schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://www.example.com/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://www.example.com/"> %s </schema> `, s) return file.Name() } func main() { doc := xsdfile(` <complexType name="BoolArray"> <complexContent> <restriction base="soapenc:Array"> <attribute ref="soapenc:arrayType" wsdl:arrayType="xs:boolean[]"/> </restriction> </complexContent> </complexType>`) var cfg xsdgen.Config cfg.Option(xsdgen.HandleSOAPArrayType()) out, err := cfg.GenSource(doc) if err != nil { log.Fatal(err) } fmt.Printf("%s\n", out) }
Output: // Code generated by ___example_test_go.test. DO NOT EDIT. package ws type BoolArray struct { Items []bool `xml:",any"` Offset string `xml:"http://schemas.xmlsoap.org/soap/encoding/ offset,attr,omitempty"` Id string `xml:"id,attr,omitempty"` Href string `xml:"href,attr,omitempty"` }
func IgnoreAttributes ¶
IgnoreAttributes defines a list of attributes that should not be declared in the Go type.
Example ¶
package main import ( "fmt" "log" "os" "github.com/henryolik/go-xml/v2/xsdgen" ) func tmpfile() *os.File { f, err := os.CreateTemp("", "xsdgen_test") if err != nil { panic(err) } return f } func xsdfile(s string) (filename string) { file := tmpfile() defer file.Close() fmt.Fprintf(file, ` <schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://www.example.com/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://www.example.com/"> %s </schema> `, s) return file.Name() } func main() { doc := xsdfile(` <complexType name="ArrayOfString"> <any maxOccurs="unbounded" /> <attribute name="soapenc:arrayType" type="xs:string" /> </complexType> `) var cfg xsdgen.Config cfg.Option(xsdgen.IgnoreAttributes("arrayType")) out, err := cfg.GenSource(doc) if err != nil { log.Fatal(err) } fmt.Printf("%s\n", out) }
Output: // Code generated by ___example_test_go.test. DO NOT EDIT. package ws type ArrayOfString struct { Items []string `xml:",any"` }
func IgnoreElements ¶
IgnoreElements defines a list of elements that should not be declared in the Go type.
Example ¶
package main import ( "fmt" "log" "os" "github.com/henryolik/go-xml/v2/xsdgen" ) func tmpfile() *os.File { f, err := os.CreateTemp("", "xsdgen_test") if err != nil { panic(err) } return f } func xsdfile(s string) (filename string) { file := tmpfile() defer file.Close() fmt.Fprintf(file, ` <schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://www.example.com/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://www.example.com/"> %s </schema> `, s) return file.Name() } func main() { doc := xsdfile(` <complexType name="Person"> <sequence> <element name="name" type="xs:string" /> <element name="deceased" type="soapenc:boolean" /> <element name="private" type="xs:int" /> </sequence> </complexType> `) var cfg xsdgen.Config cfg.Option( xsdgen.IgnoreElements("private"), xsdgen.IgnoreAttributes("id", "href")) out, err := cfg.GenSource(doc) if err != nil { log.Fatal(err) } fmt.Printf("%s\n", out) }
Output: // Code generated by ___example_test_go.test. DO NOT EDIT. package ws type Person struct { Name string `xml:"name"` Deceased bool `xml:"deceased"` }
func LogLevel ¶
LogLevel sets the verbosity of messages sent to the error log configured with the LogOutput option. The level parameter should be a positive integer between 1 and 5, with 5 providing the greatest verbosity.
func LogOutput ¶
LogOutput specifies an optional Logger for warnings and debug information about the code generation process.
Example ¶
package main import ( "log" "os" "github.com/henryolik/go-xml/v2/xsdgen" ) func main() { var cfg xsdgen.Config cfg.Option( xsdgen.LogOutput(log.New(os.Stderr, "", 0)), xsdgen.LogLevel(2)) if err := cfg.GenCLI("file.wsdl"); err != nil { log.Fatal(err) } }
Output:
func Namespaces ¶
The Namespaces option configures the code generation process to only generate code for types declared in the configured target namespaces.
func OnlyTypes ¶
OnlyTypes defines a whitelist of fully-qualified type name patterns to include in the generated Go source. Only types in the whitelist, and types that they depend on, will be included in the Go source.
func PackageName ¶
PackageName specifies the name of the generated Go package.
Example ¶
package main import ( "fmt" "log" "os" "github.com/henryolik/go-xml/v2/xsdgen" ) func tmpfile() *os.File { f, err := os.CreateTemp("", "xsdgen_test") if err != nil { panic(err) } return f } func xsdfile(s string) (filename string) { file := tmpfile() defer file.Close() fmt.Fprintf(file, ` <schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://www.example.com/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://www.example.com/"> %s </schema> `, s) return file.Name() } func main() { doc := xsdfile(` <simpleType name="zipcode"> <restriction base="xs:string"> <length value="10" /> </restriction> </simpleType> `) var cfg xsdgen.Config cfg.Option(xsdgen.PackageName("postal")) out, err := cfg.GenSource(doc) if err != nil { log.Fatal(err) } fmt.Printf("%s\n", out) }
Output: // Code generated by ___example_test_go.test. DO NOT EDIT. package postal // Must be exactly 10 items long type Zipcode string
func ProcessTypes ¶
ProcessTypes allows for users to make arbitrary changes to a type before Go source code is generated.
func Replace ¶
Replace allows for substitution rules for all identifiers to be specified. If an invalid regular expression is called, no action is taken. The Replace option is additive; subsitutions will be applied in the order that each option was applied in.
Example ¶
package main import ( "fmt" "log" "os" "github.com/henryolik/go-xml/v2/xsdgen" ) func tmpfile() *os.File { f, err := os.CreateTemp("", "xsdgen_test") if err != nil { panic(err) } return f } func xsdfile(s string) (filename string) { file := tmpfile() defer file.Close() fmt.Fprintf(file, ` <schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://www.example.com/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://www.example.com/"> %s </schema> `, s) return file.Name() } func main() { doc := xsdfile(` <complexType name="ArrayOfString"> <any maxOccurs="unbounded" /> <attribute name="soapenc:arrayType" type="xs:string" /> </complexType> `) var cfg xsdgen.Config cfg.Option(xsdgen.Replace("ArrayOf(.*)", "${1}Array")) out, err := cfg.GenSource(doc) if err != nil { log.Fatal(err) } fmt.Printf("%s\n", out) }
Output: // Code generated by ___example_test_go.test. DO NOT EDIT. package ws type StringArray struct { Items []string `xml:",any"` ArrayType string `xml:"arrayType,attr,omitempty"` }
func SOAPArrayAsSlice ¶
func SOAPArrayAsSlice() Option
SOAP 1.1 defines an Array as
<xs:complexType name="Array"> <xs:any maxOccurs="unbounded" /> <xs:attribute name="arrayType" type="xs:string" /> <!-- common attributes ellided --> </xs:complexType>
Following the normal procedure of the xsdgen package, this would map to the following Go source (with arrayType as 'int'):
type Array struct { Item []int `xml:",any"` ArrayType string `xml:"http://schemas.xmlsoap.org/soap/encoding/ arrayType"` }
While the encoding/xml package can easily marshal and unmarshal to and from such a Go type, it is not ideal to use. When using the SOAPArrayAsSlice option, if there is only one field in the Go type expression, and that field is plural, it is "unpacked". In addition, MarshalXML/UnmarshalXML methods are generated so that values can be decoded into this type. This option requires that the additional attributes ("id", "href", "offset") are either ignored or fixed by the schema.
Example ¶
package main import ( "fmt" "log" "os" "github.com/henryolik/go-xml/v2/xsdgen" ) func tmpfile() *os.File { f, err := os.CreateTemp("", "xsdgen_test") if err != nil { panic(err) } return f } func xsdfile(s string) (filename string) { file := tmpfile() defer file.Close() fmt.Fprintf(file, ` <schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://www.example.com/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://www.example.com/"> %s </schema> `, s) return file.Name() } func main() { doc := xsdfile(` <complexType name="BoolArray"> <complexContent> <restriction base="soapenc:Array"> <attribute ref="soapenc:arrayType" wsdl:arrayType="xs:boolean[]"/> </restriction> </complexContent> </complexType>`) var cfg xsdgen.Config cfg.Option( xsdgen.HandleSOAPArrayType(), xsdgen.SOAPArrayAsSlice(), xsdgen.LogOutput(log.New(os.Stderr, "", 0)), xsdgen.LogLevel(3), xsdgen.IgnoreAttributes("offset", "id", "href")) out, err := cfg.GenSource(doc) if err != nil { log.Fatal(err) } fmt.Printf("%s\n", out) }
Output: // Code generated by ___example_test_go.test. DO NOT EDIT. package ws import "encoding/xml" type BoolArray []bool func (a BoolArray) MarshalXML(e *xml.Encoder, start xml.StartElement) error { var output struct { ArrayType string `xml:"http://schemas.xmlsoap.org/wsdl/ arrayType,attr"` Items []bool `xml:" item"` } output.Items = []bool(a) start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{"", "xmlns:ns1"}, Value: "http://www.w3.org/2001/XMLSchema"}) output.ArrayType = "ns1:boolean[]" return e.EncodeElement(&output, start) } func (a *BoolArray) 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 bool if err = d.DecodeElement(&item, &tok); err == nil { *a = append(*a, item) } } if _, ok := tok.(xml.EndElement); ok { break } } return err }
func TargetNamespacesOnly ¶
TargetNamespacesOnly specifies whether or not to filter output to types generated in the namespaces defined
func UseFieldNames ¶
func UseFieldNames() Option
The UseFieldNames Option names anonymous types based on the name of the element or attribute they describe.
Example ¶
package main import ( "fmt" "log" "os" "github.com/henryolik/go-xml/v2/xsdgen" ) func tmpfile() *os.File { f, err := os.CreateTemp("", "xsdgen_test") if err != nil { panic(err) } return f } func xsdfile(s string) (filename string) { file := tmpfile() defer file.Close() fmt.Fprintf(file, ` <schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://www.example.com/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://www.example.com/"> %s </schema> `, s) return file.Name() } func main() { doc := xsdfile(` <complexType name="library"> <sequence> <element name="book" maxOccurs="unbounded"> <complexType> <all> <element name="title" type="xs:string" /> <element name="published" type="xs:date" /> <element name="author" type="xs:string" /> </all> </complexType> </element> </sequence> </complexType>`) var cfg xsdgen.Config cfg.Option(xsdgen.UseFieldNames()) out, err := cfg.GenSource(doc) if err != nil { log.Fatal(err) } fmt.Printf("%s\n", out) }
Output: // Code generated by ___example_test_go.test. DO NOT EDIT. package ws import ( "bytes" "encoding/xml" "time" ) type Book struct { Title string `xml:"title"` Published time.Time `xml:"published"` Author string `xml:"author"` } func (t *Book) MarshalXML(e *xml.Encoder, start xml.StartElement) error { type T Book var layout struct { *T Published *xsdDate `xml:"published"` } layout.T = (*T)(t) layout.Published = (*xsdDate)(&layout.T.Published) return e.EncodeElement(layout, start) } func (t *Book) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { type T Book var overlay struct { *T Published *xsdDate `xml:"published"` } overlay.T = (*T)(t) overlay.Published = (*xsdDate)(&overlay.T.Published) return d.DecodeElement(&overlay, &start) } type Library struct { Book []Book `xml:"book"` } 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 }