// A Codewalk represents a single codewalk read from an XML file.
type Codewalk struct {
- Title string "attr"
+ Title string `xml:"attr"`
File []string
Step []*Codestep
}
// A Codestep is a single step in a codewalk.
type Codestep struct {
// Filled in from XML
- Src string "attr"
- Title string "attr"
- XML string "innerxml"
+ Src string `xml:"attr"`
+ Title string `xml:"attr"`
+ XML string `xml:"innerxml"`
// Derived from Src; not in XML.
Err os.Error
"go/token"
"os"
"path/filepath"
+ "reflect"
"strconv"
"strings"
"utf8"
var err os.Error
skip, err = strconv.Atoi(name[colon+1:])
if err != nil {
- error(`illegal format for "Func:N" argument %q; %s`, name, err)
+ errorf(`illegal format for "Func:N" argument %q; %s`, name, err)
}
name = name[:colon]
}
fs := token.NewFileSet()
parsedFile, err := parser.ParseFile(fs, name, reader, 0)
if err != nil {
- error("%s: %s", name, err)
+ errorf("%s: %s", name, err)
return
}
file := &File{fs.File(parsedFile.Pos())}
done := make(chan bool)
go func() {
for e := range errors {
- error("walk error: %s", e)
+ errorf("walk error: %s", e)
}
done <- true
}()
// error formats the error to standard error, adding program
// identification and a newline
-func error(format string, args ...interface{}) {
+func errorf(format string, args ...interface{}) {
fmt.Fprintf(os.Stderr, "govet: "+format+"\n", args...)
setExit(2)
}
// Visit implements the ast.Visitor interface.
func (f *File) Visit(node ast.Node) ast.Visitor {
- // TODO: could return nil for nodes that cannot contain a CallExpr -
- // will shortcut traversal. Worthwhile?
switch n := node.(type) {
case *ast.CallExpr:
f.checkCallExpr(n)
+ case *ast.Field:
+ f.checkFieldTag(n)
}
return f
}
+// checkField checks a struct field tag.
+func (f *File) checkFieldTag(field *ast.Field) {
+ if field.Tag == nil {
+ return
+ }
+
+ tag, err := strconv.Unquote(field.Tag.Value)
+ if err != nil {
+ f.Warnf(field.Pos(), "unable to read struct tag %s", field.Tag.Value)
+ return
+ }
+
+ // Check tag for validity by appending
+ // new key:value to end and checking that
+ // the tag parsing code can find it.
+ if reflect.StructTag(tag+` _gofix:"_magic"`).Get("_gofix") != "_magic" {
+ f.Warnf(field.Pos(), "struct field tag %s not compatible with reflect.StructTag.Get", field.Tag.Value)
+ return
+ }
+}
// checkCallExpr checks a call expression.
func (f *File) checkCallExpr(call *ast.CallExpr) {
f.Warnf(0, "%s", "hello", 3) // wrong # %s in call to added function
}
+type BadTypeUsedInTests struct {
+ X int "hello" // struct field not well-formed
+}
+
// printf is used by the test.
func printf(format string, args ...interface{}) {
panic("don't call - testing only")
if i == 0 && field.Type == rawContentsType {
continue
}
- innerOffset, err = parseField(val.Field(i), innerBytes, innerOffset, parseFieldParameters(field.Tag))
+ innerOffset, err = parseField(val.Field(i), innerBytes, innerOffset, parseFieldParameters(field.Tag.Get("asn1")))
if err != nil {
return
}
}
type TestContextSpecificTags struct {
- A int "tag:1"
+ A int `asn1:"tag:1"`
}
type TestContextSpecificTags2 struct {
- A int "explicit,tag:1"
+ A int `asn1:"explicit,tag:1"`
B int
}
}
type TBSCertificate struct {
- Version int "optional,explicit,default:0,tag:0"
+ Version int `asn1:"optional,explicit,default:0,tag:0"`
SerialNumber RawValue
SignatureAlgorithm AlgorithmIdentifier
Issuer RDNSequence
for i := startingField; i < t.NumField(); i++ {
var pre *forkableWriter
pre, out = out.fork()
- err = marshalField(pre, v.Field(i), parseFieldParameters(t.Field(i).Tag))
+ err = marshalField(pre, v.Field(i), parseFieldParameters(t.Field(i).Tag.Get("asn1")))
if err != nil {
return
}
}
type implicitTagTest struct {
- A int "implicit,tag:5"
+ A int `asn1:"implicit,tag:5"`
}
type explicitTagTest struct {
- A int "explicit,tag:5"
+ A int `asn1:"explicit,tag:5"`
}
type ia5StringTest struct {
- A string "ia5"
+ A string `asn1:"ia5"`
}
type printableStringTest struct {
- A string "printable"
+ A string `asn1:"printable"`
}
type optionalRawValueTest struct {
- A RawValue "optional"
+ A RawValue `asn1:"optional"`
}
type testSET []int
type responseASN1 struct {
Status asn1.Enumerated
- Response responseBytes "explicit,tag:0"
+ Response responseBytes `asn1:"explicit,tag:0"`
}
type responseBytes struct {
TBSResponseData responseData
SignatureAlgorithm pkix.AlgorithmIdentifier
Signature asn1.BitString
- Certificates []asn1.RawValue "explicit,tag:0,optional"
+ Certificates []asn1.RawValue `asn1:"explicit,tag:0,optional"`
}
type responseData struct {
Raw asn1.RawContent
- Version int "optional,default:1,explicit,tag:0"
- RequestorName pkix.RDNSequence "optional,explicit,tag:1"
- KeyHash []byte "optional,explicit,tag:2"
+ Version int `asn1:"optional,default:1,explicit,tag:0"`
+ RequestorName pkix.RDNSequence `asn1:"optional,explicit,tag:1"`
+ KeyHash []byte `asn1:"optional,explicit,tag:2"`
ProducedAt *time.Time
Responses []singleResponse
}
type singleResponse struct {
CertID certID
- Good asn1.Flag "explicit,tag:0,optional"
- Revoked revokedInfo "explicit,tag:1,optional"
- Unknown asn1.Flag "explicit,tag:2,optional"
+ Good asn1.Flag `asn1:"explicit,tag:0,optional"`
+ Revoked revokedInfo `asn1:"explicit,tag:1,optional"`
+ Unknown asn1.Flag `asn1:"explicit,tag:2,optional"`
ThisUpdate *time.Time
- NextUpdate *time.Time "explicit,tag:0,optional"
+ NextUpdate *time.Time `asn1:"explicit,tag:0,optional"`
}
type revokedInfo struct {
RevocationTime *time.Time
- Reason int "explicit,tag:0,optional"
+ Reason int `asn1:"explicit,tag:0,optional"`
}
// This is the exposed reflection of the internal OCSP structures.
// 5280, section 4.1.1.2.
type AlgorithmIdentifier struct {
Algorithm asn1.ObjectIdentifier
- Parameters asn1.RawValue "optional"
+ Parameters asn1.RawValue `asn1:"optional"`
}
type RDNSequence []RelativeDistinguishedNameSET
// 5280, section 4.2.
type Extension struct {
Id asn1.ObjectIdentifier
- Critical bool "optional"
+ Critical bool `asn1:"optional"`
Value []byte
}
// 5280, section 5.1.
type TBSCertificateList struct {
Raw asn1.RawContent
- Version int "optional,default:2"
+ Version int `asn1:"optional,default:2"`
Signature AlgorithmIdentifier
Issuer RDNSequence
ThisUpdate *time.Time
NextUpdate *time.Time
- RevokedCertificates []RevokedCertificate "optional"
- Extensions []Extension "tag:0,optional,explicit"
+ RevokedCertificates []RevokedCertificate `asn1:"optional"`
+ Extensions []Extension `asn1:"tag:0,optional,explicit"`
}
// RevokedCertificate represents the ASN.1 structure of the same name. See RFC
type RevokedCertificate struct {
SerialNumber *big.Int
RevocationTime *time.Time
- Extensions []Extension "optional"
+ Extensions []Extension `asn1:"optional"`
}
P *big.Int
Q *big.Int
// We ignore these values, if present, because rsa will calculate them.
- Dp *big.Int "optional"
- Dq *big.Int "optional"
- Qinv *big.Int "optional"
+ Dp *big.Int `asn1:"optional"`
+ Dq *big.Int `asn1:"optional"`
+ Qinv *big.Int `asn1:"optional"`
- AdditionalPrimes []pkcs1AdditionalRSAPrime "optional"
+ AdditionalPrimes []pkcs1AdditionalRSAPrime `asn1:"optional"`
}
type pkcs1AdditionalRSAPrime struct {
type tbsCertificate struct {
Raw asn1.RawContent
- Version int "optional,explicit,default:1,tag:0"
+ Version int `asn1:"optional,explicit,default:1,tag:0"`
SerialNumber *big.Int
SignatureAlgorithm pkix.AlgorithmIdentifier
Issuer pkix.RDNSequence
Validity validity
Subject pkix.RDNSequence
PublicKey publicKeyInfo
- UniqueId asn1.BitString "optional,tag:1"
- SubjectUniqueId asn1.BitString "optional,tag:2"
- Extensions []pkix.Extension "optional,explicit,tag:3"
+ UniqueId asn1.BitString `asn1:"optional,tag:1"`
+ SubjectUniqueId asn1.BitString `asn1:"optional,tag:2"`
+ Extensions []pkix.Extension `asn1:"optional,explicit,tag:3"`
}
type dsaAlgorithmParameters struct {
// RFC 5280, 4.2.1.1
type authKeyId struct {
- Id []byte "optional,tag:0"
+ Id []byte `asn1:"optional,tag:0"`
}
type SignatureAlgorithm int
}
type basicConstraints struct {
- IsCA bool "optional"
- MaxPathLen int "optional"
+ IsCA bool `asn1:"optional"`
+ MaxPathLen int `asn1:"optional"`
}
type rsaPublicKey struct {
// RFC 5280, 4.2.1.10
type nameConstraints struct {
- Permitted []generalSubtree "optional,tag:0"
- Excluded []generalSubtree "optional,tag:1"
+ Permitted []generalSubtree `asn1:"optional,tag:0"`
+ Excluded []generalSubtree `asn1:"optional,tag:1"`
}
type generalSubtree struct {
- Name string "tag:2,optional,ia5"
- Min int "optional,tag:0"
- Max int "optional,tag:1"
+ Name string `asn1:"tag:2,optional,ia5"`
+ Min int `asn1:"optional,tag:0"`
+ Max int `asn1:"optional,tag:1"`
}
func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{}, os.Error) {
T9 struct {
a int
b, c float32
- d []string "tag"
+ d []string `go:"tag"`
}
T10 struct {
T8
if isValidTag(key) {
for i := 0; i < sv.NumField(); i++ {
f = st.Field(i)
- if f.Tag == key {
+ if f.Tag.Get("json") == key {
ok = true
break
}
type badTag struct {
X string
- Y string "y"
- Z string "@#*%(#@"
+ Y string `json:"y"`
+ Z string `x:"@#*%(#@"`
+ W string `json:"@#$@#$"`
}
type unmarshalTest struct {
{`{"x": 1}`, new(tx), tx{}, &UnmarshalFieldError{"x", txType, txType.Field(0)}},
// skip invalid tags
- {`{"X":"a", "y":"b", "Z":"c"}`, new(badTag), badTag{"a", "b", "c"}, nil},
+ {`{"X":"a", "y":"b", "Z":"c", "W":"d"}`, new(badTag), badTag{"a", "b", "c", "d"}, nil},
// syntax errors
{`{"X": "foo", "Y"}`, nil, nil, &SyntaxError{"invalid character '}' after object key", 17}},
Float32 float32
Float64 float64
- Foo string "bar"
+ Foo string `json:"bar"`
PBool *bool
PInt *int
// Array and slice values encode as JSON arrays, except that
// []byte encodes as a base64-encoded string.
//
-// Struct values encode as JSON objects. Each struct field becomes
-// a member of the object. By default the object's key name is the
-// struct field name. If the struct field has a non-empty tag consisting
-// of only Unicode letters, digits, and underscores, that tag will be used
-// as the name instead. Only exported fields will be encoded.
+// Struct values encode as JSON objects. Each exported struct field
+// becomes a member of the object. By default the object's key string
+// is the struct field name. If the struct field's tag has a "json" key with a
+// value that is a non-empty string consisting of only Unicode letters,
+// digits, and underscores, that value will be used as the object key.
+// For example, the field tag `json:"myName"` says to use "myName"
+// as the object key.
//
// Map values encode as JSON objects.
// The map's key type must be string; the object keys are used directly
} else {
e.WriteByte(',')
}
- if isValidTag(f.Tag) {
- e.string(f.Tag)
+ if tag := f.Tag.Get("json"); tag != "" && isValidTag(tag) {
+ e.string(tag)
} else {
e.string(f.Name)
}
// DNS queries.
type dnsQuestion struct {
- Name string "domain-name" // "domain-name" specifies encoding; see packers below
+ Name string `net:"domain-name"` // `net:"domain-name"` specifies encoding; see packers below
Qtype uint16
Qclass uint16
}
// There are many types of messages,
// but they all share the same header.
type dnsRR_Header struct {
- Name string "domain-name"
+ Name string `net:"domain-name"`
Rrtype uint16
Class uint16
Ttl uint32
type dnsRR_CNAME struct {
Hdr dnsRR_Header
- Cname string "domain-name"
+ Cname string `net:"domain-name"`
}
func (rr *dnsRR_CNAME) Header() *dnsRR_Header {
type dnsRR_MB struct {
Hdr dnsRR_Header
- Mb string "domain-name"
+ Mb string `net:"domain-name"`
}
func (rr *dnsRR_MB) Header() *dnsRR_Header {
type dnsRR_MG struct {
Hdr dnsRR_Header
- Mg string "domain-name"
+ Mg string `net:"domain-name"`
}
func (rr *dnsRR_MG) Header() *dnsRR_Header {
type dnsRR_MINFO struct {
Hdr dnsRR_Header
- Rmail string "domain-name"
- Email string "domain-name"
+ Rmail string `net:"domain-name"`
+ Email string `net:"domain-name"`
}
func (rr *dnsRR_MINFO) Header() *dnsRR_Header {
type dnsRR_MR struct {
Hdr dnsRR_Header
- Mr string "domain-name"
+ Mr string `net:"domain-name"`
}
func (rr *dnsRR_MR) Header() *dnsRR_Header {
type dnsRR_MX struct {
Hdr dnsRR_Header
Pref uint16
- Mx string "domain-name"
+ Mx string `net:"domain-name"`
}
func (rr *dnsRR_MX) Header() *dnsRR_Header {
type dnsRR_NS struct {
Hdr dnsRR_Header
- Ns string "domain-name"
+ Ns string `net:"domain-name"`
}
func (rr *dnsRR_NS) Header() *dnsRR_Header {
type dnsRR_PTR struct {
Hdr dnsRR_Header
- Ptr string "domain-name"
+ Ptr string `net:"domain-name"`
}
func (rr *dnsRR_PTR) Header() *dnsRR_Header {
type dnsRR_SOA struct {
Hdr dnsRR_Header
- Ns string "domain-name"
- Mbox string "domain-name"
+ Ns string `net:"domain-name"`
+ Mbox string `net:"domain-name"`
Serial uint32
Refresh uint32
Retry uint32
Priority uint16
Weight uint16
Port uint16
- Target string "domain-name"
+ Target string `net:"domain-name"`
}
func (rr *dnsRR_SRV) Header() *dnsRR_Header {
type dnsRR_A struct {
Hdr dnsRR_Header
- A uint32 "ipv4"
+ A uint32 `net:"ipv4"`
}
func (rr *dnsRR_A) Header() *dnsRR_Header {
type dnsRR_AAAA struct {
Hdr dnsRR_Header
- AAAA [16]byte "ipv6"
+ AAAA [16]byte `net:"ipv6"`
}
func (rr *dnsRR_AAAA) Header() *dnsRR_Header {
default:
fmt.Fprintf(os.Stderr, "net: dns: unknown string tag %v", f.Tag)
return len(msg), false
- case "domain-name":
+ case `net:"domain-name"`:
off, ok = packDomainName(s, msg, off)
if !ok {
return len(msg), false
default:
fmt.Fprintf(os.Stderr, "net: dns: unknown string tag %v", f.Tag)
return len(msg), false
- case "domain-name":
+ case `net:"domain-name"`:
s, off, ok = unpackDomainName(msg, off)
if !ok {
return len(msg), false
}
// Generic struct printer.
-// Doesn't care about the string tag "domain-name",
-// but does look for an "ipv4" tag on uint32 variables
-// and the "ipv6" tag on array variables,
+// Doesn't care about the string tag `net:"domain-name"`,
+// but does look for an `net:"ipv4"` tag on uint32 variables
+// and the `net:"ipv6"` tag on array variables,
// printing them as IP addresses.
func printStructValue(val reflect.Value) string {
s := "{"
fval := val.Field(i)
if fv := fval; fv.Kind() == reflect.Struct {
s += printStructValue(fv)
- } else if fv := fval; (fv.Kind() == reflect.Uint || fv.Kind() == reflect.Uint8 || fv.Kind() == reflect.Uint16 || fv.Kind() == reflect.Uint32 || fv.Kind() == reflect.Uint64 || fv.Kind() == reflect.Uintptr) && f.Tag == "ipv4" {
+ } else if fv := fval; (fv.Kind() == reflect.Uint || fv.Kind() == reflect.Uint8 || fv.Kind() == reflect.Uint16 || fv.Kind() == reflect.Uint32 || fv.Kind() == reflect.Uint64 || fv.Kind() == reflect.Uintptr) && f.Tag == `net:"ipv4"` {
i := fv.Uint()
s += IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i)).String()
- } else if fv := fval; fv.Kind() == reflect.Array && f.Tag == "ipv6" {
+ } else if fv := fval; fv.Kind() == reflect.Array && f.Tag == `net:"ipv6"` {
i := fv.Interface().([]byte)
s += IP(i).String()
} else {
},
{struct {
x struct {
- a int8 "hi there"
+ a int8 `reflect:"hi there"`
}
}{},
- `struct { a int8 "hi there" }`,
+ `struct { a int8 "reflect:\"hi there\"" }`,
},
{struct {
x struct {
- a int8 "hi \x00there\t\n\"\\"
+ a int8 `reflect:"hi \x00there\t\n\"\\"`
}
}{},
- `struct { a int8 "hi \x00there\t\n\"\\" }`,
+ `struct { a int8 "reflect:\"hi \\x00there\\t\\n\\\"\\\\\"" }`,
},
{struct {
x struct {
// make sure tag strings are not part of element type
typ = TypeOf(struct {
- d []uint32 "TAG"
+ d []uint32 `reflect:"TAG"`
}{}).Field(0).Type
testType(t, 14, typ, "[]uint32")
}
t.Errorf("after Fprintf CallSlice: %q != %q", b.String(), "hello 42 world")
}
}
+
+var tagGetTests = []struct {
+ Tag StructTag
+ Key string
+ Value string
+}{
+ {`protobuf:"PB(1,2)"`, `protobuf`, `PB(1,2)`},
+ {`protobuf:"PB(1,2)"`, `foo`, ``},
+ {`protobuf:"PB(1,2)"`, `rotobuf`, ``},
+ {`protobuf:"PB(1,2)" json:"name"`, `json`, `name`},
+ {`protobuf:"PB(1,2)" json:"name"`, `protobuf`, `PB(1,2)`},
+}
+
+func TestTagGet(t *testing.T) {
+ for _, tt := range tagGetTests {
+ if v := tt.Tag.Get(tt.Key); v != tt.Value {
+ t.Errorf("StructTag(%#q).Get(%#q) = %#q, want %#q", tt.Tag, tt.Key, v, tt.Value)
+ }
+ }
+}
// arrayType represents a fixed array type.
type arrayType struct {
- commonType "array"
+ commonType `reflect:"array"`
elem *runtime.Type
slice *runtime.Type
len uintptr
// chanType represents a channel type.
type chanType struct {
- commonType "chan"
+ commonType `reflect:"chan"`
elem *runtime.Type
dir uintptr
}
// funcType represents a function type.
type funcType struct {
- commonType "func"
+ commonType `reflect:"func"`
dotdotdot bool
in []*runtime.Type
out []*runtime.Type
// interfaceType represents an interface type.
type interfaceType struct {
- commonType "interface"
+ commonType `reflect:"interface"`
methods []imethod
}
// mapType represents a map type.
type mapType struct {
- commonType "map"
+ commonType `reflect:"map"`
key *runtime.Type
elem *runtime.Type
}
// ptrType represents a pointer type.
type ptrType struct {
- commonType "ptr"
+ commonType `reflect:"ptr"`
elem *runtime.Type
}
// sliceType represents a slice type.
type sliceType struct {
- commonType "slice"
+ commonType `reflect:"slice"`
elem *runtime.Type
}
// structType represents a struct type.
type structType struct {
- commonType "struct"
+ commonType `reflect:"struct"`
fields []structField
}
PkgPath string // empty for uppercase Name
Name string
Type Type
- Tag string
+ Tag StructTag
Offset uintptr
Index []int
Anonymous bool
}
+// A StructTag is the tag string in a struct field.
+//
+// By convention, tag strings are a concatenation of
+// optionally space-separated key:"value" pairs.
+// Each key is a non-empty string consisting of non-control
+// characters other than space (U+0020 ' '), quote (U+0022 '"'),
+// and colon (U+003A ':'). Each value is quoted using U+0022 '"'
+// characters and Go string literal syntax.
+type StructTag string
+
+// Get returns the value associated with key in the tag string.
+// If there is no such key in the tag, Get returns the empty string.
+// If the tag does not have the conventional format, the value
+// returned by Get is unspecified,
+func (tag StructTag) Get(key string) string {
+ for tag != "" {
+ // skip leading space
+ i := 0
+ for i < len(tag) && tag[i] == ' ' {
+ i++
+ }
+ tag = tag[i:]
+ if tag == "" {
+ break
+ }
+
+ // scan to colon.
+ // a space or a quote is a syntax error
+ i = 0
+ for i < len(tag) && tag[i] != ' ' && tag[i] != ':' && tag[i] != '"' {
+ i++
+ }
+ if i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' {
+ break
+ }
+ name := string(tag[:i])
+ tag = tag[i+1:]
+
+ // scan quoted string to find value
+ i = 1
+ for i < len(tag) && tag[i] != '"' {
+ if tag[i] == '\\' {
+ i++
+ }
+ i++
+ }
+ if i >= len(tag) {
+ break
+ }
+ qvalue := string(tag[:i+1])
+ tag = tag[i+1:]
+
+ if key == name {
+ value, _ := strconv.Unquote(qvalue)
+ return value
+ }
+ }
+ return ""
+}
+
// Field returns the i'th struct field.
func (t *structType) Field(i int) (f StructField) {
if i < 0 || i >= len(t.fields) {
f.PkgPath = *p.pkgPath
}
if p.tag != nil {
- f.Tag = *p.tag
+ f.Tag = StructTag(*p.tag)
}
f.Offset = p.offset
f.Index = []int{i}
func TestServer(t *testing.T) {
type addResp struct {
- Id interface{} "id"
- Result Reply "result"
- Error interface{} "error"
+ Id interface{} `json:"id"`
+ Result Reply `json:"result"`
+ Error interface{} `json:"error"`
}
cli, srv := net.Pipe()
}
type clientRequest struct {
- Method string "method"
- Params [1]interface{} "params"
- Id uint64 "id"
+ Method string `json:"method"`
+ Params [1]interface{} `json:"params"`
+ Id uint64 `json:"id"`
}
func (c *clientCodec) WriteRequest(r *rpc.Request, param interface{}) os.Error {
}
type clientResponse struct {
- Id uint64 "id"
- Result *json.RawMessage "result"
- Error interface{} "error"
+ Id uint64 `json:"id"`
+ Result *json.RawMessage `json:"result"`
+ Error interface{} `json:"error"`
}
func (r *clientResponse) reset() {
}
type serverRequest struct {
- Method string "method"
- Params *json.RawMessage "params"
- Id *json.RawMessage "id"
+ Method string `json:"method"`
+ Params *json.RawMessage `json:"params"`
+ Id *json.RawMessage `json:"id"`
}
func (r *serverRequest) reset() {
}
type serverResponse struct {
- Id *json.RawMessage "id"
- Result interface{} "result"
- Error interface{} "error"
+ Id *json.RawMessage `json:"id"`
+ Result interface{} `json:"result"`
+ Error interface{} `json:"error"`
}
func (c *serverCodec) ReadRequestHeader(r *rpc.Request) os.Error {
}
type A struct {
- XMLName Name "http://domain a"
+ XMLName Name `xml:"http://domain a"`
C
B B
FieldA string
}
type B struct {
- XMLName Name "b"
+ XMLName Name `xml:"b"`
C
FieldB string
}
}
type A2 struct {
- XMLName Name "http://domain a"
+ XMLName Name `xml:"http://domain a"`
XY string
Xy string
}
}
type A3 struct {
- XMLName Name "http://domain a"
+ XMLName Name `xml:"http://domain a"`
xy string
}
}
type A4 struct {
- XMLName Name "http://domain a"
+ XMLName Name `xml:"http://domain a"`
Any string
}
xmlns := ""
if kind == reflect.Struct {
if f, ok := typ.FieldByName("XMLName"); ok {
- if tag := f.Tag; tag != "" {
+ if tag := f.Tag.Get("xml"); tag != "" {
if i := strings.Index(tag, " "); i >= 0 {
xmlns, name = tag[:i], tag[i+1:]
} else {
}
for i, n := 0, typ.NumField(); i < n; i++ {
- if f := typ.Field(i); f.PkgPath == "" && f.Tag == "attr" {
+ if f := typ.Field(i); f.PkgPath == "" && f.Tag.Get("xml") == "attr" {
if f.Type.Kind() == reflect.String {
if str := val.Field(i).String(); str != "" {
p.WriteByte(' ')
for i, n := 0, val.NumField(); i < n; i++ {
if f := typ.Field(i); f.Name != "XMLName" && f.PkgPath == "" {
name := f.Name
- switch tag := f.Tag; tag {
+ switch tag := f.Tag.Get("xml"); tag {
case "":
case "chardata":
if tk := f.Type.Kind(); tk == reflect.String {
)
type Passenger struct {
- Name []string "name"
- Weight float32 "weight"
+ Name []string `xml:"name"`
+ Weight float32 `xml:"weight"`
}
type Ship struct {
- XMLName Name "spaceship"
+ XMLName Name `xml:"spaceship"`
- Name string "attr"
- Pilot string "attr"
- Drive DriveType "drive"
- Age uint "age"
- Passenger []*Passenger "passenger"
+ Name string `xml:"attr"`
+ Pilot string `xml:"attr"`
+ Drive DriveType `xml:"drive"`
+ Age uint `xml:"age"`
+ Passenger []*Passenger `xml:"passenger"`
secret string
}
type NamedType string
type Port struct {
- XMLName Name "port"
- Type string "attr"
- Number string "chardata"
+ XMLName Name `xml:"port"`
+ Type string `xml:"attr"`
+ Number string `xml:"chardata"`
}
type Domain struct {
- XMLName Name "domain"
- Country string "attr"
- Name []byte "chardata"
+ XMLName Name `xml:"domain"`
+ Country string `xml:"attr"`
+ Name []byte `xml:"chardata"`
}
type SecretAgent struct {
- XMLName Name "agent"
- Handle string "attr"
+ XMLName Name `xml:"agent"`
+ Handle string `xml:"attr"`
Identity string
- Obfuscate string "innerxml"
+ Obfuscate string `xml:"innerxml"`
}
var nilStruct *Ship
// For example, given these definitions:
//
// type Email struct {
-// Where string "attr"
+// Where string `xml:"attr"`
// Addr string
// }
//
// type Result struct {
-// XMLName xml.Name "result"
+// XMLName xml.Name `xml:"result"`
// Name string
// Phone string
// Email []Email
-// Groups []string "group>value"
+// Groups []string `xml:"group>value"`
// }
//
// result := Result{Name: "name", Phone: "phone", Email: nil}
// Groups was assigned considering the element path provided in the
// field tag.
//
-// Because Unmarshal uses the reflect package, it can only
-// assign to upper case fields. Unmarshal uses a case-insensitive
+// Because Unmarshal uses the reflect package, it can only assign
+// to exported (upper case) fields. Unmarshal uses a case-insensitive
// comparison to match XML element names to struct field names.
//
-// Unmarshal maps an XML element to a struct using the following rules:
+// Unmarshal maps an XML element to a struct using the following rules.
+// In the rules, the tag of a field refers to the value associated with the
+// key 'xml' in the struct field's tag (see the example above).
//
// * If the struct has a field of type []byte or string with tag "innerxml",
// Unmarshal accumulates the raw XML nested inside the element
// * If the struct has a field named XMLName of type xml.Name,
// Unmarshal records the element name in that field.
//
-// * If the XMLName field has an associated tag string of the form
-// "tag" or "namespace-URL tag", the XML element must have
-// the given tag (and, optionally, name space) or else Unmarshal
+// * If the XMLName field has an associated tag of the form
+// "name" or "namespace-URL name", the XML element must have
+// the given name (and, optionally, name space) or else Unmarshal
// returns an error.
//
// * If the XML element has an attribute whose name matches a
// field, the comments are discarded.
//
// * If the XML element contains a sub-element whose name matches
-// the prefix of a struct field tag formatted as "a>b>c", unmarshal
+// the prefix of a tag formatted as "a>b>c", unmarshal
// will descend into the XML structure looking for elements with the
// given names, and will map the innermost elements to that struct field.
-// A struct field tag starting with ">" is equivalent to one starting
+// A tag starting with ">" is equivalent to one starting
// with the field name followed by ">".
//
// * If the XML element contains a sub-element whose name
-// matches a struct field whose tag is neither "attr" nor "chardata",
+// matches a field whose tag is neither "attr" nor "chardata",
// Unmarshal maps the sub-element to that struct field.
// Otherwise, if the struct has a field named Any, unmarshal
// maps the sub-element to that struct field.
// Assign name.
if f, ok := typ.FieldByName("XMLName"); ok {
// Validate element name.
- if f.Tag != "" {
- tag := f.Tag
+ if tag := f.Tag.Get("xml"); tag != "" {
ns := ""
i := strings.LastIndex(tag, " ")
if i >= 0 {
// Also, determine whether we need to save character data or comments.
for i, n := 0, typ.NumField(); i < n; i++ {
f := typ.Field(i)
- switch f.Tag {
+ switch f.Tag.Get("xml") {
case "attr":
strv := sv.FieldByIndex(f.Index)
// Look for attribute.
}
default:
- if strings.Contains(f.Tag, ">") {
+ if tag := f.Tag.Get("xml"); strings.Contains(tag, ">") {
if fieldPaths == nil {
fieldPaths = make(map[string]pathInfo)
}
- path := strings.ToLower(f.Tag)
- if strings.HasPrefix(f.Tag, ">") {
+ path := strings.ToLower(tag)
+ if strings.HasPrefix(tag, ">") {
path = strings.ToLower(f.Name) + path
}
- if strings.HasSuffix(f.Tag, ">") {
+ if strings.HasSuffix(tag, ">") {
path = path[:len(path)-1]
}
err := addFieldPath(sv, fieldPaths, path, f.Index)
t := sv.Type()
f1 := t.FieldByIndex(idx1)
f2 := t.FieldByIndex(idx2)
- return &TagPathError{t, f1.Name, f1.Tag, f2.Name, f2.Tag}
+ return &TagPathError{t, f1.Name, f1.Tag.Get("xml"), f2.Name, f2.Tag.Get("xml")}
}
// unmarshalPaths walks down an XML structure looking for
</summary></entry></feed> `
type Feed struct {
- XMLName Name "http://www.w3.org/2005/Atom feed"
+ XMLName Name `xml:"http://www.w3.org/2005/Atom feed"`
Title string
Id string
Link []Link
}
type Link struct {
- Rel string "attr"
- Href string "attr"
+ Rel string `xml:"attr"`
+ Href string `xml:"attr"`
}
type Person struct {
Name string
URI string
Email string
- InnerXML string "innerxml"
+ InnerXML string `xml:"innerxml"`
}
type Text struct {
- Type string "attr"
- Body string "chardata"
+ Type string `xml:"attr"`
+ Body string `xml:"chardata"`
}
type Time string
}
type PathTestA struct {
- Items []PathTestItem ">item1"
+ Items []PathTestItem `xml:">item1"`
Before, After string
}
type PathTestB struct {
- Other []PathTestItem "items>Item1"
+ Other []PathTestItem `xml:"items>Item1"`
Before, After string
}
type PathTestC struct {
- Values1 []string "items>item1>value"
- Values2 []string "items>item2>value"
+ Values1 []string `xml:"items>item1>value"`
+ Values2 []string `xml:"items>item2>value"`
Before, After string
}
}
type PathTestD struct {
- Other PathTestSet "items>"
+ Other PathTestSet `xml:"items>"`
Before, After string
}
}
type BadPathTestA struct {
- First string "items>item1"
- Other string "items>item2"
- Second string "items>"
+ First string `xml:"items>item1"`
+ Other string `xml:"items>item2"`
+ Second string `xml:"items>"`
}
type BadPathTestB struct {
- Other string "items>item2>value"
- First string "items>item1"
- Second string "items>item1>value"
+ Other string `xml:"items>item2>value"`
+ First string `xml:"items>item1"`
+ Second string `xml:"items>item1>value"`
}
var badPathTests = []struct {
}
type Test1 struct {
- Int int "attr"
- Float float64 "attr"
- Uint8 uint8 "attr"
+ Int int `xml:"attr"`
+ Float float64 `xml:"attr"`
+ Uint8 uint8 `xml:"attr"`
}
type Test2 struct {
- Bool bool "attr"
+ Bool bool `xml:"attr"`
}
const attrString = `