xml: aqwari.net/xml/xsdgen Index | Examples | Files

package xsdgen

import "aqwari.net/xml/xsdgen"

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

Examples

Package Files

builtin.go cli.go config.go doc.go xsdgen.go

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.

type Code Uses

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 Uses

func (c *Code) DocType(targetNS string) (*xsd.ComplexType, bool)

DocType retrieves the complexType for the provided target namespace.

func (*Code) GenAST Uses

func (code *Code) GenAST() (*ast.File, error)

GenAST generates a Go abstract syntax tree with the type declarations contained in the xml schema document.

func (*Code) NameOf Uses

func (c *Code) NameOf(name xml.Name) string

NameOf returns the Go identifier associated with the canonical XML type.

type Config Uses

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 Uses

func (cfg *Config) GenAST(files ...string) (*ast.File, error)

GenAST creates an *ast.File containing type declarations and associated methods based on a set of XML schema.

func (*Config) GenCLI Uses

func (cfg *Config) GenCLI(arguments ...string) error

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.

Code:

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)
}

func (*Config) GenCode Uses

func (cfg *Config) GenCode(data ...[]byte) (*Code, error)

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 Uses

func (cfg *Config) GenSource(files ...string) ([]byte, error)

The GenSource method converts the AST returned by GenAST to formatted Go source code.

func (*Config) NameOf Uses

func (cfg *Config) NameOf(name xml.Name) string

NameOf converts a canonical XML name to a Go identifier, applying any user-provided filters.

func (*Config) Option Uses

func (cfg *Config) Option(opts ...Option) (previous Option)

The Option method is used to configure an existing configuration. The return value of the Option method can be used to revert the final option to its previous setting.

type Logger Uses

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 Uses

type Option func(*Config) Option

An Option is used to customize a Config.

func AllowType Uses

func AllowType(name xml.Name) Option

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 HandleSOAPArrayType Uses

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.

Code:

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:

package ws

type BoolArray struct {
	Items  []bool `xml:",any"`
	Offset string `xml:"offset,attr,omitempty"`
	Id     string `xml:"id,attr,omitempty"`
	Href   string `xml:"href,attr,omitempty"`
}

func IgnoreAttributes Uses

func IgnoreAttributes(names ...string) Option

IgnoreAttributes defines a list of attributes that should not be declared in the Go type.

Code:

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:

package ws

type ArrayOfString struct {
	Items []string `xml:",any"`
}

func IgnoreElements Uses

func IgnoreElements(names ...string) Option

IgnoreElements defines a list of elements that should not be declared in the Go type.

Code:

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:

package ws

type Person struct {
	Name     string `xml:"http://www.example.com/ name"`
	Deceased bool   `xml:"http://www.example.com/ deceased"`
}

func LogLevel Uses

func LogLevel(level int) Option

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 Uses

func LogOutput(l Logger) Option

LogOutput specifies an optional Logger for warnings and debug information about the code generation process.

Code:

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)
}

func Namespaces Uses

func Namespaces(xmlns ...string) Option

The Namespaces option configures the code generation process to only generate code for types declared in the configured target namespaces.

func OnlyTypes Uses

func OnlyTypes(patterns ...string) Option

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 Uses

func PackageName(name string) Option

PackageName specifies the name of the generated Go package.

Code:

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:

package postal

// May be no more than 10 items long
type Zipcode string

func ProcessTypes Uses

func ProcessTypes(fn func(xsd.Schema, xsd.Type) xsd.Type) Option

ProcessTypes allows for users to make arbitrary changes to a type before Go source code is generated.

func Replace Uses

func Replace(pat, repl string) Option

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.

Code:

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:

package ws

type StringArray struct {
	Items     []string `xml:",any"`
	ArrayType string   `xml:"arrayType,attr,omitempty"`
}

func SOAPArrayAsSlice Uses

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.

Code:

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:

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 UseFieldNames Uses

func UseFieldNames() Option

The UseFieldNames Option names anonymous types based on the name of the element or attribute they describe.

Code:

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:

package ws

import (
	"bytes"
	"encoding/xml"
	"time"
)

type Book struct {
	Title     string    `xml:"http://www.example.com/ title"`
	Published time.Time `xml:"http://www.example.com/ published"`
	Author    string    `xml:"http://www.example.com/ author"`
}

func (t *Book) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
	type T Book
	var layout struct {
		*T
		Published *xsdDate `xml:"http://www.example.com/ 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:"http://www.example.com/ published"`
	}
	overlay.T = (*T)(t)
	overlay.Published = (*xsdDate)(&overlay.T.Published)
	return d.DecodeElement(&overlay, &start)
}

type Library struct {
	Book []Book `xml:"http://www.example.com/ 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 []byte((time.Time)(t).Format("2006-01-02")), nil
}
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
}

Package xsdgen imports 19 packages (graph) and is imported by 3 packages. Updated 2018-06-18. Refresh now. Tools for package owners.