]> Cypherpunks repositories - gostls13.git/commitdiff
reflect: support for struct tag use by multiple packages
authorRuss Cox <rsc@golang.org>
Wed, 29 Jun 2011 13:52:34 +0000 (09:52 -0400)
committerRuss Cox <rsc@golang.org>
Wed, 29 Jun 2011 13:52:34 +0000 (09:52 -0400)
Each package using struct field tags assumes that
it is the only package storing data in the tag.
This CL adds support in package reflect for sharing
tags between multiple packages.  In this scheme, the
tags must be of the form

        key:"value" key2:"value2"

(raw strings help when writing that tag in Go source).

reflect.StructField's Tag field now has type StructTag
(a string type), which has method Get(key string) string
that returns the associated value.

Clients of json and xml will need to be updated.
Code that says

        type T struct {
                X int "name"
        }

should become

        type T struct {
                X int `json:"name"`  // or `xml:"name"`
        }

Use govet to identify struct tags that need to be changed
to use the new syntax.

R=r, r, dsymonds, bradfitz, kevlar, fvbommel, n13m3y3r
CC=golang-dev
https://golang.org/cl/4645069

24 files changed:
src/cmd/godoc/codewalk.go
src/cmd/govet/govet.go
src/pkg/asn1/asn1.go
src/pkg/asn1/asn1_test.go
src/pkg/asn1/marshal.go
src/pkg/asn1/marshal_test.go
src/pkg/crypto/ocsp/ocsp.go
src/pkg/crypto/x509/pkix/pkix.go
src/pkg/crypto/x509/x509.go
src/pkg/go/types/testdata/exports.go
src/pkg/json/decode.go
src/pkg/json/decode_test.go
src/pkg/json/encode.go
src/pkg/net/dnsmsg.go
src/pkg/reflect/all_test.go
src/pkg/reflect/type.go
src/pkg/rpc/jsonrpc/all_test.go
src/pkg/rpc/jsonrpc/client.go
src/pkg/rpc/jsonrpc/server.go
src/pkg/xml/embed_test.go
src/pkg/xml/marshal.go
src/pkg/xml/marshal_test.go
src/pkg/xml/read.go
src/pkg/xml/read_test.go

index 54bebe854f4a3cc471b667bf03925ede7ea71e9d..74178cecd0e08af9d0df016523d8ff9f14bee374 100644 (file)
@@ -74,7 +74,7 @@ func codewalk(w http.ResponseWriter, r *http.Request) {
 
 // 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
 }
@@ -83,9 +83,9 @@ type Codewalk struct {
 // 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
index 73bd2faeffaede30668883d0e7d003656149ca3c..28652676fd700d86aa1f498fa2e526ed6a361b8b 100644 (file)
@@ -15,6 +15,7 @@ import (
        "go/token"
        "os"
        "path/filepath"
+       "reflect"
        "strconv"
        "strings"
        "utf8"
@@ -59,7 +60,7 @@ func main() {
                                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]
                        }
@@ -93,7 +94,7 @@ func doFile(name string, reader io.Reader) {
        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())}
@@ -121,7 +122,7 @@ func walkDir(root string) {
        done := make(chan bool)
        go func() {
                for e := range errors {
-                       error("walk error: %s", e)
+                       errorf("walk error: %s", e)
                }
                done <- true
        }()
@@ -132,7 +133,7 @@ func walkDir(root string) {
 
 // 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)
 }
@@ -185,15 +186,35 @@ func (f *File) checkFile(name string, file *ast.File) {
 
 // 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) {
@@ -373,6 +394,10 @@ func BadFunctionUsedInTests() {
        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")
index 2650ef2a2685204748b29698a99c56578eeda808..95f299e63d7d3a9afd885cf02c86979e122f0206 100644 (file)
@@ -707,7 +707,7 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
                        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
                        }
index 463dbe0264920678a395c50ceef7426c1805310a..3c9478618e653db30e76db52b74abb96dc7236b1 100644 (file)
@@ -299,11 +299,11 @@ type TestObjectIdentifierStruct struct {
 }
 
 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
 }
 
@@ -353,7 +353,7 @@ type Certificate struct {
 }
 
 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
index 7212c91ef98b944cfdad0c2a21590de63a120924..d7eb63bf82c9f1531f4ae617086276b771a5540d 100644 (file)
@@ -413,7 +413,7 @@ func marshalBody(out *forkableWriter, value reflect.Value, params fieldParameter
                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
                        }
index a9517634d88cb1850ca0079bdcc398522e77f916..03df5f1e1d52b86fd24eb7ebd45a57f64b57ae2c 100644 (file)
@@ -30,23 +30,23 @@ type rawContentsStruct struct {
 }
 
 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
index 57dbe7d2d99b0539beedd29db617a99cb4d7a404..e725bded81c86d12c1e9b7ddee0d29455a4c9f5b 100644 (file)
@@ -43,7 +43,7 @@ type certID struct {
 
 type responseASN1 struct {
        Status   asn1.Enumerated
-       Response responseBytes "explicit,tag:0"
+       Response responseBytes `asn1:"explicit,tag:0"`
 }
 
 type responseBytes struct {
@@ -55,30 +55,30 @@ type basicResponse 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.
index 7806b2a2eb8c88f84c839547b8fff0732dd9ed2b..266fd557a52c98af090341b5a7c6e10ea35dc9be 100644 (file)
@@ -16,7 +16,7 @@ import (
 // 5280, section 4.1.1.2.
 type AlgorithmIdentifier struct {
        Algorithm  asn1.ObjectIdentifier
-       Parameters asn1.RawValue "optional"
+       Parameters asn1.RawValue `asn1:"optional"`
 }
 
 type RDNSequence []RelativeDistinguishedNameSET
@@ -32,7 +32,7 @@ type AttributeTypeAndValue struct {
 // 5280, section 4.2.
 type Extension struct {
        Id       asn1.ObjectIdentifier
-       Critical bool "optional"
+       Critical bool `asn1:"optional"`
        Value    []byte
 }
 
@@ -149,13 +149,13 @@ func (certList *CertificateList) HasExpired(currentTimeSeconds int64) bool {
 // 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
@@ -163,5 +163,5 @@ type TBSCertificateList struct {
 type RevokedCertificate struct {
        SerialNumber   *big.Int
        RevocationTime *time.Time
-       Extensions     []Extension "optional"
+       Extensions     []Extension `asn1:"optional"`
 }
index 8bafeda5c9c026ad11bef1ed7f46a44804549ff3..348727a26e6e43eceb158f97535b0becdae5cf78 100644 (file)
@@ -30,11 +30,11 @@ type pkcs1PrivateKey struct {
        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 {
@@ -136,16 +136,16 @@ type certificate 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 {
@@ -168,7 +168,7 @@ type publicKeyInfo struct {
 
 // RFC 5280,  4.2.1.1
 type authKeyId struct {
-       Id []byte "optional,tag:0"
+       Id []byte `asn1:"optional,tag:0"`
 }
 
 type SignatureAlgorithm int
@@ -480,8 +480,8 @@ func (h UnhandledCriticalExtension) String() string {
 }
 
 type basicConstraints struct {
-       IsCA       bool "optional"
-       MaxPathLen int  "optional"
+       IsCA       bool `asn1:"optional"`
+       MaxPathLen int  `asn1:"optional"`
 }
 
 type rsaPublicKey struct {
@@ -497,14 +497,14 @@ type policyInformation 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) {
index 1de2e00ad8486621ffa5cc21e2d0227be33d83ab..035a13fb700082c553891de7afaaa315b35d87c3 100644 (file)
@@ -38,7 +38,7 @@ type (
        T9  struct {
                a    int
                b, c float32
-               d    []string "tag"
+               d    []string `go:"tag"`
        }
        T10 struct {
                T8
index e78b60ccb54c0e2b154f8ab182fbdb6349608570..35a06b0f9640cfbfe0a8d60e99fd1e6484a089ec 100644 (file)
@@ -482,7 +482,7 @@ func (d *decodeState) object(v reflect.Value) {
                        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
                                        }
index bf8bf10bf89b10582c0d5fc5f4c24ed40f05b7a7..9b84bc76c477e1b44049cd206f8287f3d0da7854 100644 (file)
@@ -42,8 +42,9 @@ var (
 
 type badTag struct {
        X string
-       Y string "y"
-       Z string "@#*%(#@"
+       Y string `json:"y"`
+       Z string `x:"@#*%(#@"`
+       W string `json:"@#$@#$"`
 }
 
 type unmarshalTest struct {
@@ -68,7 +69,7 @@ var unmarshalTests = []unmarshalTest{
        {`{"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}},
@@ -250,7 +251,7 @@ type All struct {
        Float32 float32
        Float64 float64
 
-       Foo string "bar"
+       Foo string `json:"bar"`
 
        PBool    *bool
        PInt     *int
index ec0a14a6a4d77077a8a96b1f1e5da54bd7dccea6..adc0f0f371ec3cf89bcdbc424f53d395ac8634d1 100644 (file)
@@ -36,11 +36,13 @@ import (
 // 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
@@ -236,8 +238,8 @@ func (e *encodeState) reflectValue(v reflect.Value) {
                        } 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)
                        }
index ade1bb3a97da78e46f468d053eb606e8b30d1667..640973b13a0dc4b4d68990317671a4d72545a434 100644 (file)
@@ -93,7 +93,7 @@ const (
 
 // 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
 }
@@ -102,7 +102,7 @@ type dnsQuestion struct {
 // 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
@@ -121,7 +121,7 @@ type dnsRR interface {
 
 type dnsRR_CNAME struct {
        Hdr   dnsRR_Header
-       Cname string "domain-name"
+       Cname string `net:"domain-name"`
 }
 
 func (rr *dnsRR_CNAME) Header() *dnsRR_Header {
@@ -140,7 +140,7 @@ func (rr *dnsRR_HINFO) 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 {
@@ -149,7 +149,7 @@ 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 {
@@ -158,8 +158,8 @@ 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 {
@@ -168,7 +168,7 @@ 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 {
@@ -178,7 +178,7 @@ 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 {
@@ -187,7 +187,7 @@ 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 {
@@ -196,7 +196,7 @@ 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 {
@@ -205,8 +205,8 @@ 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
@@ -232,7 +232,7 @@ type dnsRR_SRV struct {
        Priority uint16
        Weight   uint16
        Port     uint16
-       Target   string "domain-name"
+       Target   string `net:"domain-name"`
 }
 
 func (rr *dnsRR_SRV) Header() *dnsRR_Header {
@@ -241,7 +241,7 @@ 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 {
@@ -250,7 +250,7 @@ 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 {
@@ -435,7 +435,7 @@ func packStructValue(val reflect.Value, msg []byte, off int) (off1 int, ok bool)
                        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
@@ -506,7 +506,7 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, ok boo
                        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
@@ -536,9 +536,9 @@ func unpackStruct(any interface{}, msg []byte, off int) (off1 int, ok bool) {
 }
 
 // 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 := "{"
@@ -553,10 +553,10 @@ func printStructValue(val reflect.Value) string {
                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 {
index 94b0fb5b361f107b06b02dc5183dbe6295e7b14e..34d74b37a4dccab7345c8eb5542814b3e4c78027 100644 (file)
@@ -127,17 +127,17 @@ var typeTests = []pair{
        },
        {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 {
@@ -423,7 +423,7 @@ func TestAll(t *testing.T) {
 
        // 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")
 }
@@ -1544,3 +1544,23 @@ func TestVariadic(t *testing.T) {
                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)
+               }
+       }
+}
index f774f730752706d288e5edd47434ed023a5d061a..a120da732a8f5a806f720b483cdd7fa251f61c57 100644 (file)
@@ -274,7 +274,7 @@ const (
 
 // arrayType represents a fixed array type.
 type arrayType struct {
-       commonType "array"
+       commonType `reflect:"array"`
        elem       *runtime.Type
        slice      *runtime.Type
        len        uintptr
@@ -282,14 +282,14 @@ type arrayType struct {
 
 // 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
@@ -304,26 +304,26 @@ type imethod struct {
 
 // 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
 }
 
@@ -338,7 +338,7 @@ type structField struct {
 
 // structType represents a struct type.
 type structType struct {
-       commonType "struct"
+       commonType `reflect:"struct"`
        fields     []structField
 }
 
@@ -696,12 +696,72 @@ type StructField struct {
        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) {
@@ -723,7 +783,7 @@ func (t *structType) Field(i int) (f StructField) {
                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}
index 02b9735eb1000ae396a8349234dee423a89d3044..c1a9e8ecbc5395f3e968ab5938b2396b23a7a26d 100644 (file)
@@ -51,9 +51,9 @@ func init() {
 
 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()
index 57e977d3253194c801c5252da58f86cd2d25db7e..577d0ce4295b60095f1af6cd379e693156d967ce 100644 (file)
@@ -44,9 +44,9 @@ func NewClientCodec(conn io.ReadWriteCloser) rpc.ClientCodec {
 }
 
 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 {
@@ -60,9 +60,9 @@ 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() {
index 9c6b8b40d68daa1e4b1d0cf5859320e4c54b652e..9801fdf221ef5471d29b79a8b8bfbce4b1384409 100644 (file)
@@ -43,9 +43,9 @@ func NewServerCodec(conn io.ReadWriteCloser) rpc.ServerCodec {
 }
 
 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() {
@@ -59,9 +59,9 @@ 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 {
index abfe781acdf18a8a453e39bded11128bda843d35..ec7f478bec3fdff5fbe049660402232dd3098748 100644 (file)
@@ -12,14 +12,14 @@ type C struct {
 }
 
 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
 }
@@ -65,7 +65,7 @@ func TestEmbedded1(t *testing.T) {
 }
 
 type A2 struct {
-       XMLName Name "http://domain a"
+       XMLName Name `xml:"http://domain a"`
        XY      string
        Xy      string
 }
@@ -92,7 +92,7 @@ func TestEmbedded2(t *testing.T) {
 }
 
 type A3 struct {
-       XMLName Name "http://domain a"
+       XMLName Name `xml:"http://domain a"`
        xy      string
 }
 
@@ -108,7 +108,7 @@ func TestEmbedded3(t *testing.T) {
 }
 
 type A4 struct {
-       XMLName Name "http://domain a"
+       XMLName Name `xml:"http://domain a"`
        Any     string
 }
 
index d3a1f95367d766ec22c8a44cb4a4c03e7d200a8e..2ac03a91e2d8b21bcea5c8b3d5a1387fcb74f9ad 100644 (file)
@@ -108,7 +108,7 @@ func (p *printer) marshalValue(val reflect.Value, name string) os.Error {
        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 {
@@ -132,7 +132,7 @@ func (p *printer) marshalValue(val reflect.Value, name string) os.Error {
                }
 
                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(' ')
@@ -173,7 +173,7 @@ func (p *printer) marshalValue(val reflect.Value, name string) os.Error {
                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 {
index 3408f6d5084068eb5bacb2f20ed5229f266ad85c..77b2e726d5ad6ad54d00e55e0bc128985c919b56 100644 (file)
@@ -22,18 +22,18 @@ const (
 )
 
 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
 }
 
@@ -46,22 +46,22 @@ func (rx RawXML) MarshalXML() ([]byte, os.Error) {
 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
index 427c311583603866d0dc72530bf64acc1c437da3..786b69f5a3258f9a4f151a78c81901fb18c8bd5c 100644 (file)
@@ -31,16 +31,16 @@ import (
 // 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}
@@ -79,11 +79,13 @@ import (
 // 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
@@ -92,9 +94,9 @@ import (
 //   * 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
@@ -112,14 +114,14 @@ import (
 //      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.
@@ -297,8 +299,7 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error {
                // 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 {
@@ -330,7 +331,7 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error {
                // 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.
@@ -366,15 +367,15 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error {
                                }
 
                        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)
@@ -574,7 +575,7 @@ func tagError(sv reflect.Value, idx1 []int, idx2 []int) os.Error {
        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
index e07cb153104d0a6a1c44c229bafa48b3738ac595..2126da3c751fd8b8e46a4add65fd5bd3288295f4 100644 (file)
@@ -78,7 +78,7 @@ not being used from outside intra_region_diff.py.
 </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
@@ -97,20 +97,20 @@ type Entry struct {
 }
 
 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
@@ -255,18 +255,18 @@ type PathTestItem struct {
 }
 
 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
 }
 
@@ -275,7 +275,7 @@ type PathTestSet struct {
 }
 
 type PathTestD struct {
-       Other         PathTestSet "items>"
+       Other         PathTestSet `xml:"items>"`
        Before, After string
 }
 
@@ -299,15 +299,15 @@ func TestUnmarshalPaths(t *testing.T) {
 }
 
 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 {
@@ -342,13 +342,13 @@ type AttrTest 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 = `