// Generate a type declaration for the bult-in list values, along with // marshal/unmarshal methods func (cfg *Config) genTokenListSpec(t xsd.Builtin) ([]spec, error) { cfg.debugf("generating Go source for token list %q", xsd.XMLName(t).Local) s := spec{ name: strings.ToLower(t.String()), expr: builtinExpr(t), xsdType: t, } marshal, err := gen.Func("MarshalText"). Receiver("x "+s.name). Returns("[]byte", "error"). Body(` return []byte(strings.Join(x, " ")), nil `).Decl() if err != nil { return nil, fmt.Errorf("MarshalText %s: %v", s.name, err) } unmarshal, err := gen.Func("UnmarshalText"). Receiver("x " + s.name). Args("text []byte"). Returns("error"). Body(` *x = bytes.Fields(text) return nil `).Decl() if err != nil { return nil, fmt.Errorf("UnmarshalText %s: %v", s.name, err) } s.methods = append(s.methods, marshal, unmarshal) return []spec{s}, nil }
// Generate a type declaration for the built-in time values, along with // marshal/unmarshal methods for them. func (cfg *Config) genTimeSpec(t xsd.Builtin) ([]spec, error) { var timespec string cfg.debugf("generating Go source for time type %q", xsd.XMLName(t).Local) s := spec{ expr: ast.NewIdent("time.Time"), name: builtinExpr(t).(*ast.Ident).Name, xsdType: t, } switch t { case xsd.GDay: timespec = "---02" case xsd.GMonth: timespec = "--01" case xsd.GMonthDay: timespec = "--01-02" case xsd.GYear: timespec = "2006" case xsd.GYearMonth: timespec = "2006-01" case xsd.Time: timespec = "15:04:05.999999999" case xsd.Date: timespec = "2006-01-02" case xsd.DateTime: timespec = "2006-01-02T15:04:05.999999999" } unmarshal, err := gen.Func("UnmarshalText"). Receiver("t *"+s.name). Args("text []byte"). Returns("error"). Body(` return _unmarshalTime(text, (*time.Time)(t), %q) `, timespec).Decl() if err != nil { return nil, fmt.Errorf("could not generate unmarshal function for %s: %v", s.name, err) } marshal, err := gen.Func("MarshalText"). Receiver("t *"+s.name). Returns("[]byte", "error"). Body(` return []byte((*time.Time)(t).Format(%q)), nil `, timespec).Decl() if err != nil { return nil, fmt.Errorf("could not generate marshal function for %s: %v", s.name, err) } s.methods = append(s.methods, unmarshal, marshal) if helper := cfg.helper("_unmarshalTime"); helper != nil { //panic(fmt.Sprint("adding ", helper.Name.Name, " to functions for ", s.name)) s.methods = append(s.methods, helper) } return []spec{s}, nil }
// Generate a type declaration for the built-in binary values, along with // marshal/unmarshal methods for them. func (cfg *Config) genBinarySpec(t xsd.Builtin) ([]spec, error) { cfg.debugf("generating Go source for binary type %q", xsd.XMLName(t).Local) s := spec{ expr: builtinExpr(t), name: xsd.XMLName(t).Local, xsdType: t, } marshal := gen.Func("MarshalText").Receiver("b "+s.name).Returns("[]byte", "error") unmarshal := gen.Func("UnmarshalText").Receiver("b " + s.name).Args("text []byte"). Returns("err error") switch t { case xsd.HexBinary: unmarshal.Body(` *b, err = hex.DecodeString(string(text)) return err `) marshal.Body(` n := hex.EncodedLen([]byte(b)) buf := make([]byte, n) hex.Encode(buf, []byte(b)) return buf, nil `) case xsd.Base64Binary: unmarshal.Body(` *b, err = base64.StdEncoding.DecodeString(string(text)) return err `) marshal.Body(` var buf bytes.Buffer enc := base64.NewEncoder(base64.StdEncoding, &buf) enc.Write([]byte(b)) enc.Close() return buf.Bytes() `) } marshalFn, err := marshal.Decl() if err != nil { return nil, fmt.Errorf("MarshalText %s: %v", s.name, err) } unmarshalFn, err := unmarshal.Decl() if err != nil { return nil, fmt.Errorf("UnmarshalText %s: %v", s.name, err) } s.methods = append(s.methods, unmarshalFn, marshalFn) return []spec{s}, nil }
// Generate a type declaration for a <list> type, along with marshal/unmarshal // methods. func (cfg *Config) genSimpleListSpec(t *xsd.SimpleType) ([]spec, error) { cfg.debugf("generating Go source for simple list %q", xsd.XMLName(t).Local) expr, err := cfg.expr(t.Base) if err != nil { return nil, err } s := spec{ name: cfg.typeName(t.Name), expr: expr, xsdType: t, } marshal, err := gen.Func("MarshalText"). Receiver("x *"+s.name). Returns("[]byte", "error"). Body(` return nil, nil `).Decl() if err != nil { return nil, fmt.Errorf("MarshalText %s: %v", s.name, err) } unmarshal, err := gen.Func("UnmarshalText"). Receiver("x *" + s.name). Args("text []byte"). Returns("error"). Body(` return nil `).Decl() if err != nil { return nil, fmt.Errorf("UnmarshalText %s: %v", s.name, err) } s.methods = append(s.methods, marshal, unmarshal) return []spec{s}, nil }
func (cfg *Config) addStandardHelpers() { fns := []*gen.Function{ gen.Func("_unmarshalTime"). Args("text []byte", "t *time.Time", "format string"). Returns("err error"). Body(` 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 `), } for _, fn := range fns { x, err := fn.Decl() if err != nil { // These functions are all bundled with the program, and // should never fail to parse panic("failed to create helper function: " + err.Error()) } cfg.helpers = append(cfg.helpers, x) } }
// SOAP arrays (and other similar types) are complex types with a single // plural element. We add a post-processing step to flatten it out and provide // marshal/unmarshal methods. func (cfg *Config) soapArrayToSlice(s spec) spec { str, ok := s.expr.(*ast.StructType) if !ok { return s } if len(str.Fields.List) != 1 { return s } slice, ok := str.Fields.List[0].Type.(*ast.ArrayType) if !ok { return s } cfg.debugf("flattening single-element slice struct type %s to []%v", s.name, slice.Elt) tag := gen.TagKey(str.Fields.List[0], "xml") xmltag := xml.Name{"", ",any"} if tag != "" { parts := strings.Split(tag, ",") if len(parts) > 0 { fields := strings.Fields(parts[0]) if len(fields) > 0 { xmltag.Local = fields[len(fields)-1] } if len(fields) > 1 { xmltag.Space = fields[0] } } } itemType := gen.ExprString(slice.Elt) unmarshal, err := gen.Func("UnmarshalXML"). Receiver("a *"+s.name). Args("d *xml.Decoder", "start xml.StartElement"). Returns("err error"). Body(` var tok xml.Token var itemTag = xml.Name{%q, %q} for tok, err = d.Token(); err == nil; tok, err = d.Token() { if tok, ok := tok.(xml.StartElement); ok { var item %s 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 `, xmltag.Space, xmltag.Local, itemType).Decl() if err != nil { cfg.logf("error generating UnmarshalXML method of %s: %v", s.name, err) return s } marshal, err := gen.Func("MarshalXML"). Receiver("a *"+s.name). Args("e *xml.Encoder", "start xml.StartElement"). Returns("error"). Body(` tag := xml.StartElement{Name: xml.Name{"", "item"}} for _, elt := range *a { if err := e.EncodeElement(elt, tag); err != nil { return err } } return nil `).Decl() if err != nil { cfg.logf("error generating MarshalXML method of %s: %v", s.name, err) return s } s.expr = slice s.methods = append(s.methods, marshal) s.methods = append(s.methods, unmarshal) if helper := cfg.helper("_unmarshalArray"); helper != nil { s.methods = append(s.methods, helper) } return s }