xsdgen

package
v0.0.0-...-03ebdbe Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 15, 2016 License: MIT Imports: 18 Imported by: 0

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

Examples

Constants

This section is empty.

Variables

View Source
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 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

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

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.

Example
package main

import (
	"log"

	"github.com/lajonat/go-xml/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) GenSource

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

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

func (*Config) Option

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

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

type Option func(*Config) Option

An Option is used to customize a Config.

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"
	"io/ioutil"
	"log"
	"os"

	"github.com/lajonat/go-xml/xsdgen"
)

func tmpfile() *os.File {
	f, err := ioutil.TempFile("", "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:

package ws

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

func IgnoreAttributes

func IgnoreAttributes(names ...string) Option

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

Example
package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"

	"github.com/lajonat/go-xml/xsdgen"
)

func tmpfile() *os.File {
	f, err := ioutil.TempFile("", "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:

package ws

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

func IgnoreElements

func IgnoreElements(names ...string) Option

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

Example
package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"

	"github.com/lajonat/go-xml/xsdgen"
)

func tmpfile() *os.File {
	f, err := ioutil.TempFile("", "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:

package ws

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

func LogLevel

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

func LogOutput(l Logger) Option

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

Example
package main

import (
	"log"
	"os"

	"github.com/lajonat/go-xml/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

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

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

func PackageName(name string) Option

PackageName specifies the name of the generated Go package.

Example
package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"

	"github.com/lajonat/go-xml/xsdgen"
)

func tmpfile() *os.File {
	f, err := ioutil.TempFile("", "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:

package postal

type Zipcode string

func ProcessTypes

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

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.

Example
package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"

	"github.com/lajonat/go-xml/xsdgen"
)

func tmpfile() *os.File {
	f, err := ioutil.TempFile("", "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:

package ws

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

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"
	"io/ioutil"
	"log"
	"os"

	"github.com/lajonat/go-xml/xsdgen"
)

func tmpfile() *os.File {
	f, err := ioutil.TempFile("", "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.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 {
	tag := xml.StartElement{Name: xml.Name{"", "item"}}
	for _, elt := range *a {
		if err := e.EncodeElement(elt, tag); err != nil {
			return err
		}
	}
	return nil
}
func (a *BoolArray) UnmarshalXML(d *xml.Decoder, start xml.StartElement) (err error) {
	var tok xml.Token
	var itemTag = xml.Name{"", ",any"}
	for tok, err = d.Token(); err == nil; tok, err = d.Token() {
		if tok, ok := tok.(xml.StartElement); ok {
			var item bool
			if itemTag.Local != ",any" && itemTag != tok.Name {
				err = d.Skip()
				continue
			}
			if err = d.DecodeElement(&item, &tok); err == nil {
				*a = append(*a, item)
			}
		}
		if _, ok := tok.(xml.EndElement); ok {
			break
		}
	}
	return err
}

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"
	"io/ioutil"
	"log"
	"os"

	"github.com/lajonat/go-xml/xsdgen"
)

func tmpfile() *os.File {
	f, err := ioutil.TempFile("", "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:

package ws

import (
	"bytes"
	"time"
)

type Book struct {
	Title     string  `xml:"http://www.example.com/ title"`
	Published xsdDate `xml:"http://www.example.com/ published"`
	Author    string  `xml:"http://www.example.com/ author"`
}
type Library struct {
	Book      []Book  `xml:"http://www.example.com/ book"`
	Title     string  `xml:"http://www.example.com/ title"`
	Published xsdDate `xml:"http://www.example.com/ published"`
	Author    string  `xml:"http://www.example.com/ author"`
}
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 _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
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL