1
0
mirror of https://github.com/golang/go synced 2024-11-25 02:07:58 -07:00

reflect: support for struct tag use by multiple packages

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
This commit is contained in:
Russ Cox 2011-06-29 09:52:34 -04:00
parent f83609f642
commit 25733a94fd
24 changed files with 293 additions and 184 deletions

View File

@ -74,7 +74,7 @@ func codewalk(w http.ResponseWriter, r *http.Request) {
// A Codewalk represents a single codewalk read from an XML file. // A Codewalk represents a single codewalk read from an XML file.
type Codewalk struct { type Codewalk struct {
Title string "attr" Title string `xml:"attr"`
File []string File []string
Step []*Codestep Step []*Codestep
} }
@ -83,9 +83,9 @@ type Codewalk struct {
// A Codestep is a single step in a codewalk. // A Codestep is a single step in a codewalk.
type Codestep struct { type Codestep struct {
// Filled in from XML // Filled in from XML
Src string "attr" Src string `xml:"attr"`
Title string "attr" Title string `xml:"attr"`
XML string "innerxml" XML string `xml:"innerxml"`
// Derived from Src; not in XML. // Derived from Src; not in XML.
Err os.Error Err os.Error

View File

@ -15,6 +15,7 @@ import (
"go/token" "go/token"
"os" "os"
"path/filepath" "path/filepath"
"reflect"
"strconv" "strconv"
"strings" "strings"
"utf8" "utf8"
@ -59,7 +60,7 @@ func main() {
var err os.Error var err os.Error
skip, err = strconv.Atoi(name[colon+1:]) skip, err = strconv.Atoi(name[colon+1:])
if err != nil { 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] name = name[:colon]
} }
@ -93,7 +94,7 @@ func doFile(name string, reader io.Reader) {
fs := token.NewFileSet() fs := token.NewFileSet()
parsedFile, err := parser.ParseFile(fs, name, reader, 0) parsedFile, err := parser.ParseFile(fs, name, reader, 0)
if err != nil { if err != nil {
error("%s: %s", name, err) errorf("%s: %s", name, err)
return return
} }
file := &File{fs.File(parsedFile.Pos())} file := &File{fs.File(parsedFile.Pos())}
@ -121,7 +122,7 @@ func walkDir(root string) {
done := make(chan bool) done := make(chan bool)
go func() { go func() {
for e := range errors { for e := range errors {
error("walk error: %s", e) errorf("walk error: %s", e)
} }
done <- true done <- true
}() }()
@ -132,7 +133,7 @@ func walkDir(root string) {
// error formats the error to standard error, adding program // error formats the error to standard error, adding program
// identification and a newline // identification and a newline
func error(format string, args ...interface{}) { func errorf(format string, args ...interface{}) {
fmt.Fprintf(os.Stderr, "govet: "+format+"\n", args...) fmt.Fprintf(os.Stderr, "govet: "+format+"\n", args...)
setExit(2) setExit(2)
} }
@ -185,15 +186,35 @@ func (f *File) checkFile(name string, file *ast.File) {
// Visit implements the ast.Visitor interface. // Visit implements the ast.Visitor interface.
func (f *File) Visit(node ast.Node) ast.Visitor { 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) { switch n := node.(type) {
case *ast.CallExpr: case *ast.CallExpr:
f.checkCallExpr(n) f.checkCallExpr(n)
case *ast.Field:
f.checkFieldTag(n)
} }
return f 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. // checkCallExpr checks a call expression.
func (f *File) checkCallExpr(call *ast.CallExpr) { 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 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. // printf is used by the test.
func printf(format string, args ...interface{}) { func printf(format string, args ...interface{}) {
panic("don't call - testing only") panic("don't call - testing only")

View File

@ -707,7 +707,7 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
if i == 0 && field.Type == rawContentsType { if i == 0 && field.Type == rawContentsType {
continue 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 { if err != nil {
return return
} }

View File

@ -299,11 +299,11 @@ type TestObjectIdentifierStruct struct {
} }
type TestContextSpecificTags struct { type TestContextSpecificTags struct {
A int "tag:1" A int `asn1:"tag:1"`
} }
type TestContextSpecificTags2 struct { type TestContextSpecificTags2 struct {
A int "explicit,tag:1" A int `asn1:"explicit,tag:1"`
B int B int
} }
@ -353,7 +353,7 @@ type Certificate struct {
} }
type TBSCertificate struct { type TBSCertificate struct {
Version int "optional,explicit,default:0,tag:0" Version int `asn1:"optional,explicit,default:0,tag:0"`
SerialNumber RawValue SerialNumber RawValue
SignatureAlgorithm AlgorithmIdentifier SignatureAlgorithm AlgorithmIdentifier
Issuer RDNSequence Issuer RDNSequence

View File

@ -413,7 +413,7 @@ func marshalBody(out *forkableWriter, value reflect.Value, params fieldParameter
for i := startingField; i < t.NumField(); i++ { for i := startingField; i < t.NumField(); i++ {
var pre *forkableWriter var pre *forkableWriter
pre, out = out.fork() 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 { if err != nil {
return return
} }

View File

@ -30,23 +30,23 @@ type rawContentsStruct struct {
} }
type implicitTagTest struct { type implicitTagTest struct {
A int "implicit,tag:5" A int `asn1:"implicit,tag:5"`
} }
type explicitTagTest struct { type explicitTagTest struct {
A int "explicit,tag:5" A int `asn1:"explicit,tag:5"`
} }
type ia5StringTest struct { type ia5StringTest struct {
A string "ia5" A string `asn1:"ia5"`
} }
type printableStringTest struct { type printableStringTest struct {
A string "printable" A string `asn1:"printable"`
} }
type optionalRawValueTest struct { type optionalRawValueTest struct {
A RawValue "optional" A RawValue `asn1:"optional"`
} }
type testSET []int type testSET []int

View File

@ -43,7 +43,7 @@ type certID struct {
type responseASN1 struct { type responseASN1 struct {
Status asn1.Enumerated Status asn1.Enumerated
Response responseBytes "explicit,tag:0" Response responseBytes `asn1:"explicit,tag:0"`
} }
type responseBytes struct { type responseBytes struct {
@ -55,30 +55,30 @@ type basicResponse struct {
TBSResponseData responseData TBSResponseData responseData
SignatureAlgorithm pkix.AlgorithmIdentifier SignatureAlgorithm pkix.AlgorithmIdentifier
Signature asn1.BitString Signature asn1.BitString
Certificates []asn1.RawValue "explicit,tag:0,optional" Certificates []asn1.RawValue `asn1:"explicit,tag:0,optional"`
} }
type responseData struct { type responseData struct {
Raw asn1.RawContent Raw asn1.RawContent
Version int "optional,default:1,explicit,tag:0" Version int `asn1:"optional,default:1,explicit,tag:0"`
RequestorName pkix.RDNSequence "optional,explicit,tag:1" RequestorName pkix.RDNSequence `asn1:"optional,explicit,tag:1"`
KeyHash []byte "optional,explicit,tag:2" KeyHash []byte `asn1:"optional,explicit,tag:2"`
ProducedAt *time.Time ProducedAt *time.Time
Responses []singleResponse Responses []singleResponse
} }
type singleResponse struct { type singleResponse struct {
CertID certID CertID certID
Good asn1.Flag "explicit,tag:0,optional" Good asn1.Flag `asn1:"explicit,tag:0,optional"`
Revoked revokedInfo "explicit,tag:1,optional" Revoked revokedInfo `asn1:"explicit,tag:1,optional"`
Unknown asn1.Flag "explicit,tag:2,optional" Unknown asn1.Flag `asn1:"explicit,tag:2,optional"`
ThisUpdate *time.Time ThisUpdate *time.Time
NextUpdate *time.Time "explicit,tag:0,optional" NextUpdate *time.Time `asn1:"explicit,tag:0,optional"`
} }
type revokedInfo struct { type revokedInfo struct {
RevocationTime *time.Time 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. // This is the exposed reflection of the internal OCSP structures.

View File

@ -16,7 +16,7 @@ import (
// 5280, section 4.1.1.2. // 5280, section 4.1.1.2.
type AlgorithmIdentifier struct { type AlgorithmIdentifier struct {
Algorithm asn1.ObjectIdentifier Algorithm asn1.ObjectIdentifier
Parameters asn1.RawValue "optional" Parameters asn1.RawValue `asn1:"optional"`
} }
type RDNSequence []RelativeDistinguishedNameSET type RDNSequence []RelativeDistinguishedNameSET
@ -32,7 +32,7 @@ type AttributeTypeAndValue struct {
// 5280, section 4.2. // 5280, section 4.2.
type Extension struct { type Extension struct {
Id asn1.ObjectIdentifier Id asn1.ObjectIdentifier
Critical bool "optional" Critical bool `asn1:"optional"`
Value []byte Value []byte
} }
@ -149,13 +149,13 @@ func (certList *CertificateList) HasExpired(currentTimeSeconds int64) bool {
// 5280, section 5.1. // 5280, section 5.1.
type TBSCertificateList struct { type TBSCertificateList struct {
Raw asn1.RawContent Raw asn1.RawContent
Version int "optional,default:2" Version int `asn1:"optional,default:2"`
Signature AlgorithmIdentifier Signature AlgorithmIdentifier
Issuer RDNSequence Issuer RDNSequence
ThisUpdate *time.Time ThisUpdate *time.Time
NextUpdate *time.Time NextUpdate *time.Time
RevokedCertificates []RevokedCertificate "optional" RevokedCertificates []RevokedCertificate `asn1:"optional"`
Extensions []Extension "tag:0,optional,explicit" Extensions []Extension `asn1:"tag:0,optional,explicit"`
} }
// RevokedCertificate represents the ASN.1 structure of the same name. See RFC // RevokedCertificate represents the ASN.1 structure of the same name. See RFC
@ -163,5 +163,5 @@ type TBSCertificateList struct {
type RevokedCertificate struct { type RevokedCertificate struct {
SerialNumber *big.Int SerialNumber *big.Int
RevocationTime *time.Time RevocationTime *time.Time
Extensions []Extension "optional" Extensions []Extension `asn1:"optional"`
} }

View File

@ -30,11 +30,11 @@ type pkcs1PrivateKey struct {
P *big.Int P *big.Int
Q *big.Int Q *big.Int
// We ignore these values, if present, because rsa will calculate them. // We ignore these values, if present, because rsa will calculate them.
Dp *big.Int "optional" Dp *big.Int `asn1:"optional"`
Dq *big.Int "optional" Dq *big.Int `asn1:"optional"`
Qinv *big.Int "optional" Qinv *big.Int `asn1:"optional"`
AdditionalPrimes []pkcs1AdditionalRSAPrime "optional" AdditionalPrimes []pkcs1AdditionalRSAPrime `asn1:"optional"`
} }
type pkcs1AdditionalRSAPrime struct { type pkcs1AdditionalRSAPrime struct {
@ -136,16 +136,16 @@ type certificate struct {
type tbsCertificate struct { type tbsCertificate struct {
Raw asn1.RawContent Raw asn1.RawContent
Version int "optional,explicit,default:1,tag:0" Version int `asn1:"optional,explicit,default:1,tag:0"`
SerialNumber *big.Int SerialNumber *big.Int
SignatureAlgorithm pkix.AlgorithmIdentifier SignatureAlgorithm pkix.AlgorithmIdentifier
Issuer pkix.RDNSequence Issuer pkix.RDNSequence
Validity validity Validity validity
Subject pkix.RDNSequence Subject pkix.RDNSequence
PublicKey publicKeyInfo PublicKey publicKeyInfo
UniqueId asn1.BitString "optional,tag:1" UniqueId asn1.BitString `asn1:"optional,tag:1"`
SubjectUniqueId asn1.BitString "optional,tag:2" SubjectUniqueId asn1.BitString `asn1:"optional,tag:2"`
Extensions []pkix.Extension "optional,explicit,tag:3" Extensions []pkix.Extension `asn1:"optional,explicit,tag:3"`
} }
type dsaAlgorithmParameters struct { type dsaAlgorithmParameters struct {
@ -168,7 +168,7 @@ type publicKeyInfo struct {
// RFC 5280, 4.2.1.1 // RFC 5280, 4.2.1.1
type authKeyId struct { type authKeyId struct {
Id []byte "optional,tag:0" Id []byte `asn1:"optional,tag:0"`
} }
type SignatureAlgorithm int type SignatureAlgorithm int
@ -480,8 +480,8 @@ func (h UnhandledCriticalExtension) String() string {
} }
type basicConstraints struct { type basicConstraints struct {
IsCA bool "optional" IsCA bool `asn1:"optional"`
MaxPathLen int "optional" MaxPathLen int `asn1:"optional"`
} }
type rsaPublicKey struct { type rsaPublicKey struct {
@ -497,14 +497,14 @@ type policyInformation struct {
// RFC 5280, 4.2.1.10 // RFC 5280, 4.2.1.10
type nameConstraints struct { type nameConstraints struct {
Permitted []generalSubtree "optional,tag:0" Permitted []generalSubtree `asn1:"optional,tag:0"`
Excluded []generalSubtree "optional,tag:1" Excluded []generalSubtree `asn1:"optional,tag:1"`
} }
type generalSubtree struct { type generalSubtree struct {
Name string "tag:2,optional,ia5" Name string `asn1:"tag:2,optional,ia5"`
Min int "optional,tag:0" Min int `asn1:"optional,tag:0"`
Max int "optional,tag:1" Max int `asn1:"optional,tag:1"`
} }
func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{}, os.Error) { func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{}, os.Error) {

View File

@ -38,7 +38,7 @@ type (
T9 struct { T9 struct {
a int a int
b, c float32 b, c float32
d []string "tag" d []string `go:"tag"`
} }
T10 struct { T10 struct {
T8 T8

View File

@ -482,7 +482,7 @@ func (d *decodeState) object(v reflect.Value) {
if isValidTag(key) { if isValidTag(key) {
for i := 0; i < sv.NumField(); i++ { for i := 0; i < sv.NumField(); i++ {
f = st.Field(i) f = st.Field(i)
if f.Tag == key { if f.Tag.Get("json") == key {
ok = true ok = true
break break
} }

View File

@ -42,8 +42,9 @@ var (
type badTag struct { type badTag struct {
X string X string
Y string "y" Y string `json:"y"`
Z string "@#*%(#@" Z string `x:"@#*%(#@"`
W string `json:"@#$@#$"`
} }
type unmarshalTest struct { type unmarshalTest struct {
@ -68,7 +69,7 @@ var unmarshalTests = []unmarshalTest{
{`{"x": 1}`, new(tx), tx{}, &UnmarshalFieldError{"x", txType, txType.Field(0)}}, {`{"x": 1}`, new(tx), tx{}, &UnmarshalFieldError{"x", txType, txType.Field(0)}},
// skip invalid tags // 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 // syntax errors
{`{"X": "foo", "Y"}`, nil, nil, &SyntaxError{"invalid character '}' after object key", 17}}, {`{"X": "foo", "Y"}`, nil, nil, &SyntaxError{"invalid character '}' after object key", 17}},
@ -250,7 +251,7 @@ type All struct {
Float32 float32 Float32 float32
Float64 float64 Float64 float64
Foo string "bar" Foo string `json:"bar"`
PBool *bool PBool *bool
PInt *int PInt *int

View File

@ -36,11 +36,13 @@ import (
// Array and slice values encode as JSON arrays, except that // Array and slice values encode as JSON arrays, except that
// []byte encodes as a base64-encoded string. // []byte encodes as a base64-encoded string.
// //
// Struct values encode as JSON objects. Each struct field becomes // Struct values encode as JSON objects. Each exported struct field
// a member of the object. By default the object's key name is the // becomes a member of the object. By default the object's key string
// struct field name. If the struct field has a non-empty tag consisting // is the struct field name. If the struct field's tag has a "json" key with a
// of only Unicode letters, digits, and underscores, that tag will be used // value that is a non-empty string consisting of only Unicode letters,
// as the name instead. Only exported fields will be encoded. // 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. // Map values encode as JSON objects.
// The map's key type must be string; the object keys are used directly // 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 { } else {
e.WriteByte(',') e.WriteByte(',')
} }
if isValidTag(f.Tag) { if tag := f.Tag.Get("json"); tag != "" && isValidTag(tag) {
e.string(f.Tag) e.string(tag)
} else { } else {
e.string(f.Name) e.string(f.Name)
} }

View File

@ -93,7 +93,7 @@ const (
// DNS queries. // DNS queries.
type dnsQuestion struct { 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 Qtype uint16
Qclass uint16 Qclass uint16
} }
@ -102,7 +102,7 @@ type dnsQuestion struct {
// There are many types of messages, // There are many types of messages,
// but they all share the same header. // but they all share the same header.
type dnsRR_Header struct { type dnsRR_Header struct {
Name string "domain-name" Name string `net:"domain-name"`
Rrtype uint16 Rrtype uint16
Class uint16 Class uint16
Ttl uint32 Ttl uint32
@ -121,7 +121,7 @@ type dnsRR interface {
type dnsRR_CNAME struct { type dnsRR_CNAME struct {
Hdr dnsRR_Header Hdr dnsRR_Header
Cname string "domain-name" Cname string `net:"domain-name"`
} }
func (rr *dnsRR_CNAME) Header() *dnsRR_Header { func (rr *dnsRR_CNAME) Header() *dnsRR_Header {
@ -140,7 +140,7 @@ func (rr *dnsRR_HINFO) Header() *dnsRR_Header {
type dnsRR_MB struct { type dnsRR_MB struct {
Hdr dnsRR_Header Hdr dnsRR_Header
Mb string "domain-name" Mb string `net:"domain-name"`
} }
func (rr *dnsRR_MB) Header() *dnsRR_Header { func (rr *dnsRR_MB) Header() *dnsRR_Header {
@ -149,7 +149,7 @@ func (rr *dnsRR_MB) Header() *dnsRR_Header {
type dnsRR_MG struct { type dnsRR_MG struct {
Hdr dnsRR_Header Hdr dnsRR_Header
Mg string "domain-name" Mg string `net:"domain-name"`
} }
func (rr *dnsRR_MG) Header() *dnsRR_Header { func (rr *dnsRR_MG) Header() *dnsRR_Header {
@ -158,8 +158,8 @@ func (rr *dnsRR_MG) Header() *dnsRR_Header {
type dnsRR_MINFO struct { type dnsRR_MINFO struct {
Hdr dnsRR_Header Hdr dnsRR_Header
Rmail string "domain-name" Rmail string `net:"domain-name"`
Email string "domain-name" Email string `net:"domain-name"`
} }
func (rr *dnsRR_MINFO) Header() *dnsRR_Header { func (rr *dnsRR_MINFO) Header() *dnsRR_Header {
@ -168,7 +168,7 @@ func (rr *dnsRR_MINFO) Header() *dnsRR_Header {
type dnsRR_MR struct { type dnsRR_MR struct {
Hdr dnsRR_Header Hdr dnsRR_Header
Mr string "domain-name" Mr string `net:"domain-name"`
} }
func (rr *dnsRR_MR) Header() *dnsRR_Header { func (rr *dnsRR_MR) Header() *dnsRR_Header {
@ -178,7 +178,7 @@ func (rr *dnsRR_MR) Header() *dnsRR_Header {
type dnsRR_MX struct { type dnsRR_MX struct {
Hdr dnsRR_Header Hdr dnsRR_Header
Pref uint16 Pref uint16
Mx string "domain-name" Mx string `net:"domain-name"`
} }
func (rr *dnsRR_MX) Header() *dnsRR_Header { func (rr *dnsRR_MX) Header() *dnsRR_Header {
@ -187,7 +187,7 @@ func (rr *dnsRR_MX) Header() *dnsRR_Header {
type dnsRR_NS struct { type dnsRR_NS struct {
Hdr dnsRR_Header Hdr dnsRR_Header
Ns string "domain-name" Ns string `net:"domain-name"`
} }
func (rr *dnsRR_NS) Header() *dnsRR_Header { func (rr *dnsRR_NS) Header() *dnsRR_Header {
@ -196,7 +196,7 @@ func (rr *dnsRR_NS) Header() *dnsRR_Header {
type dnsRR_PTR struct { type dnsRR_PTR struct {
Hdr dnsRR_Header Hdr dnsRR_Header
Ptr string "domain-name" Ptr string `net:"domain-name"`
} }
func (rr *dnsRR_PTR) Header() *dnsRR_Header { func (rr *dnsRR_PTR) Header() *dnsRR_Header {
@ -205,8 +205,8 @@ func (rr *dnsRR_PTR) Header() *dnsRR_Header {
type dnsRR_SOA struct { type dnsRR_SOA struct {
Hdr dnsRR_Header Hdr dnsRR_Header
Ns string "domain-name" Ns string `net:"domain-name"`
Mbox string "domain-name" Mbox string `net:"domain-name"`
Serial uint32 Serial uint32
Refresh uint32 Refresh uint32
Retry uint32 Retry uint32
@ -232,7 +232,7 @@ type dnsRR_SRV struct {
Priority uint16 Priority uint16
Weight uint16 Weight uint16
Port uint16 Port uint16
Target string "domain-name" Target string `net:"domain-name"`
} }
func (rr *dnsRR_SRV) Header() *dnsRR_Header { func (rr *dnsRR_SRV) Header() *dnsRR_Header {
@ -241,7 +241,7 @@ func (rr *dnsRR_SRV) Header() *dnsRR_Header {
type dnsRR_A struct { type dnsRR_A struct {
Hdr dnsRR_Header Hdr dnsRR_Header
A uint32 "ipv4" A uint32 `net:"ipv4"`
} }
func (rr *dnsRR_A) Header() *dnsRR_Header { func (rr *dnsRR_A) Header() *dnsRR_Header {
@ -250,7 +250,7 @@ func (rr *dnsRR_A) Header() *dnsRR_Header {
type dnsRR_AAAA struct { type dnsRR_AAAA struct {
Hdr dnsRR_Header Hdr dnsRR_Header
AAAA [16]byte "ipv6" AAAA [16]byte `net:"ipv6"`
} }
func (rr *dnsRR_AAAA) Header() *dnsRR_Header { 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: default:
fmt.Fprintf(os.Stderr, "net: dns: unknown string tag %v", f.Tag) fmt.Fprintf(os.Stderr, "net: dns: unknown string tag %v", f.Tag)
return len(msg), false return len(msg), false
case "domain-name": case `net:"domain-name"`:
off, ok = packDomainName(s, msg, off) off, ok = packDomainName(s, msg, off)
if !ok { if !ok {
return len(msg), false return len(msg), false
@ -506,7 +506,7 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, ok boo
default: default:
fmt.Fprintf(os.Stderr, "net: dns: unknown string tag %v", f.Tag) fmt.Fprintf(os.Stderr, "net: dns: unknown string tag %v", f.Tag)
return len(msg), false return len(msg), false
case "domain-name": case `net:"domain-name"`:
s, off, ok = unpackDomainName(msg, off) s, off, ok = unpackDomainName(msg, off)
if !ok { if !ok {
return len(msg), false return len(msg), false
@ -536,9 +536,9 @@ func unpackStruct(any interface{}, msg []byte, off int) (off1 int, ok bool) {
} }
// Generic struct printer. // Generic struct printer.
// Doesn't care about the string tag "domain-name", // Doesn't care about the string tag `net:"domain-name"`,
// but does look for an "ipv4" tag on uint32 variables // but does look for an `net:"ipv4"` tag on uint32 variables
// and the "ipv6" tag on array variables, // and the `net:"ipv6"` tag on array variables,
// printing them as IP addresses. // printing them as IP addresses.
func printStructValue(val reflect.Value) string { func printStructValue(val reflect.Value) string {
s := "{" s := "{"
@ -553,10 +553,10 @@ func printStructValue(val reflect.Value) string {
fval := val.Field(i) fval := val.Field(i)
if fv := fval; fv.Kind() == reflect.Struct { if fv := fval; fv.Kind() == reflect.Struct {
s += printStructValue(fv) 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() i := fv.Uint()
s += IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i)).String() 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) i := fv.Interface().([]byte)
s += IP(i).String() s += IP(i).String()
} else { } else {

View File

@ -127,17 +127,17 @@ var typeTests = []pair{
}, },
{struct { {struct {
x struct { x struct {
a int8 "hi there" a int8 `reflect:"hi there"`
} }
}{}, }{},
`struct { a int8 "hi there" }`, `struct { a int8 "reflect:\"hi there\"" }`,
}, },
{struct { {struct {
x 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 { {struct {
x struct { x struct {
@ -423,7 +423,7 @@ func TestAll(t *testing.T) {
// make sure tag strings are not part of element type // make sure tag strings are not part of element type
typ = TypeOf(struct { typ = TypeOf(struct {
d []uint32 "TAG" d []uint32 `reflect:"TAG"`
}{}).Field(0).Type }{}).Field(0).Type
testType(t, 14, typ, "[]uint32") 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") 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)
}
}
}

View File

@ -274,7 +274,7 @@ const (
// arrayType represents a fixed array type. // arrayType represents a fixed array type.
type arrayType struct { type arrayType struct {
commonType "array" commonType `reflect:"array"`
elem *runtime.Type elem *runtime.Type
slice *runtime.Type slice *runtime.Type
len uintptr len uintptr
@ -282,14 +282,14 @@ type arrayType struct {
// chanType represents a channel type. // chanType represents a channel type.
type chanType struct { type chanType struct {
commonType "chan" commonType `reflect:"chan"`
elem *runtime.Type elem *runtime.Type
dir uintptr dir uintptr
} }
// funcType represents a function type. // funcType represents a function type.
type funcType struct { type funcType struct {
commonType "func" commonType `reflect:"func"`
dotdotdot bool dotdotdot bool
in []*runtime.Type in []*runtime.Type
out []*runtime.Type out []*runtime.Type
@ -304,26 +304,26 @@ type imethod struct {
// interfaceType represents an interface type. // interfaceType represents an interface type.
type interfaceType struct { type interfaceType struct {
commonType "interface" commonType `reflect:"interface"`
methods []imethod methods []imethod
} }
// mapType represents a map type. // mapType represents a map type.
type mapType struct { type mapType struct {
commonType "map" commonType `reflect:"map"`
key *runtime.Type key *runtime.Type
elem *runtime.Type elem *runtime.Type
} }
// ptrType represents a pointer type. // ptrType represents a pointer type.
type ptrType struct { type ptrType struct {
commonType "ptr" commonType `reflect:"ptr"`
elem *runtime.Type elem *runtime.Type
} }
// sliceType represents a slice type. // sliceType represents a slice type.
type sliceType struct { type sliceType struct {
commonType "slice" commonType `reflect:"slice"`
elem *runtime.Type elem *runtime.Type
} }
@ -338,7 +338,7 @@ type structField struct {
// structType represents a struct type. // structType represents a struct type.
type structType struct { type structType struct {
commonType "struct" commonType `reflect:"struct"`
fields []structField fields []structField
} }
@ -696,12 +696,72 @@ type StructField struct {
PkgPath string // empty for uppercase Name PkgPath string // empty for uppercase Name
Name string Name string
Type Type Type Type
Tag string Tag StructTag
Offset uintptr Offset uintptr
Index []int Index []int
Anonymous bool 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. // Field returns the i'th struct field.
func (t *structType) Field(i int) (f StructField) { func (t *structType) Field(i int) (f StructField) {
if i < 0 || i >= len(t.fields) { if i < 0 || i >= len(t.fields) {
@ -723,7 +783,7 @@ func (t *structType) Field(i int) (f StructField) {
f.PkgPath = *p.pkgPath f.PkgPath = *p.pkgPath
} }
if p.tag != nil { if p.tag != nil {
f.Tag = *p.tag f.Tag = StructTag(*p.tag)
} }
f.Offset = p.offset f.Offset = p.offset
f.Index = []int{i} f.Index = []int{i}

View File

@ -51,9 +51,9 @@ func init() {
func TestServer(t *testing.T) { func TestServer(t *testing.T) {
type addResp struct { type addResp struct {
Id interface{} "id" Id interface{} `json:"id"`
Result Reply "result" Result Reply `json:"result"`
Error interface{} "error" Error interface{} `json:"error"`
} }
cli, srv := net.Pipe() cli, srv := net.Pipe()

View File

@ -44,9 +44,9 @@ func NewClientCodec(conn io.ReadWriteCloser) rpc.ClientCodec {
} }
type clientRequest struct { type clientRequest struct {
Method string "method" Method string `json:"method"`
Params [1]interface{} "params" Params [1]interface{} `json:"params"`
Id uint64 "id" Id uint64 `json:"id"`
} }
func (c *clientCodec) WriteRequest(r *rpc.Request, param interface{}) os.Error { 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 { type clientResponse struct {
Id uint64 "id" Id uint64 `json:"id"`
Result *json.RawMessage "result" Result *json.RawMessage `json:"result"`
Error interface{} "error" Error interface{} `json:"error"`
} }
func (r *clientResponse) reset() { func (r *clientResponse) reset() {

View File

@ -43,9 +43,9 @@ func NewServerCodec(conn io.ReadWriteCloser) rpc.ServerCodec {
} }
type serverRequest struct { type serverRequest struct {
Method string "method" Method string `json:"method"`
Params *json.RawMessage "params" Params *json.RawMessage `json:"params"`
Id *json.RawMessage "id" Id *json.RawMessage `json:"id"`
} }
func (r *serverRequest) reset() { func (r *serverRequest) reset() {
@ -59,9 +59,9 @@ func (r *serverRequest) reset() {
} }
type serverResponse struct { type serverResponse struct {
Id *json.RawMessage "id" Id *json.RawMessage `json:"id"`
Result interface{} "result" Result interface{} `json:"result"`
Error interface{} "error" Error interface{} `json:"error"`
} }
func (c *serverCodec) ReadRequestHeader(r *rpc.Request) os.Error { func (c *serverCodec) ReadRequestHeader(r *rpc.Request) os.Error {

View File

@ -12,14 +12,14 @@ type C struct {
} }
type A struct { type A struct {
XMLName Name "http://domain a" XMLName Name `xml:"http://domain a"`
C C
B B B B
FieldA string FieldA string
} }
type B struct { type B struct {
XMLName Name "b" XMLName Name `xml:"b"`
C C
FieldB string FieldB string
} }
@ -65,7 +65,7 @@ func TestEmbedded1(t *testing.T) {
} }
type A2 struct { type A2 struct {
XMLName Name "http://domain a" XMLName Name `xml:"http://domain a"`
XY string XY string
Xy string Xy string
} }
@ -92,7 +92,7 @@ func TestEmbedded2(t *testing.T) {
} }
type A3 struct { type A3 struct {
XMLName Name "http://domain a" XMLName Name `xml:"http://domain a"`
xy string xy string
} }
@ -108,7 +108,7 @@ func TestEmbedded3(t *testing.T) {
} }
type A4 struct { type A4 struct {
XMLName Name "http://domain a" XMLName Name `xml:"http://domain a"`
Any string Any string
} }

View File

@ -108,7 +108,7 @@ func (p *printer) marshalValue(val reflect.Value, name string) os.Error {
xmlns := "" xmlns := ""
if kind == reflect.Struct { if kind == reflect.Struct {
if f, ok := typ.FieldByName("XMLName"); ok { 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 { if i := strings.Index(tag, " "); i >= 0 {
xmlns, name = tag[:i], tag[i+1:] xmlns, name = tag[:i], tag[i+1:]
} else { } 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++ { 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 f.Type.Kind() == reflect.String {
if str := val.Field(i).String(); str != "" { if str := val.Field(i).String(); str != "" {
p.WriteByte(' ') 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++ { for i, n := 0, val.NumField(); i < n; i++ {
if f := typ.Field(i); f.Name != "XMLName" && f.PkgPath == "" { if f := typ.Field(i); f.Name != "XMLName" && f.PkgPath == "" {
name := f.Name name := f.Name
switch tag := f.Tag; tag { switch tag := f.Tag.Get("xml"); tag {
case "": case "":
case "chardata": case "chardata":
if tk := f.Type.Kind(); tk == reflect.String { if tk := f.Type.Kind(); tk == reflect.String {

View File

@ -22,18 +22,18 @@ const (
) )
type Passenger struct { type Passenger struct {
Name []string "name" Name []string `xml:"name"`
Weight float32 "weight" Weight float32 `xml:"weight"`
} }
type Ship struct { type Ship struct {
XMLName Name "spaceship" XMLName Name `xml:"spaceship"`
Name string "attr" Name string `xml:"attr"`
Pilot string "attr" Pilot string `xml:"attr"`
Drive DriveType "drive" Drive DriveType `xml:"drive"`
Age uint "age" Age uint `xml:"age"`
Passenger []*Passenger "passenger" Passenger []*Passenger `xml:"passenger"`
secret string secret string
} }
@ -46,22 +46,22 @@ func (rx RawXML) MarshalXML() ([]byte, os.Error) {
type NamedType string type NamedType string
type Port struct { type Port struct {
XMLName Name "port" XMLName Name `xml:"port"`
Type string "attr" Type string `xml:"attr"`
Number string "chardata" Number string `xml:"chardata"`
} }
type Domain struct { type Domain struct {
XMLName Name "domain" XMLName Name `xml:"domain"`
Country string "attr" Country string `xml:"attr"`
Name []byte "chardata" Name []byte `xml:"chardata"`
} }
type SecretAgent struct { type SecretAgent struct {
XMLName Name "agent" XMLName Name `xml:"agent"`
Handle string "attr" Handle string `xml:"attr"`
Identity string Identity string
Obfuscate string "innerxml" Obfuscate string `xml:"innerxml"`
} }
var nilStruct *Ship var nilStruct *Ship

View File

@ -31,16 +31,16 @@ import (
// For example, given these definitions: // For example, given these definitions:
// //
// type Email struct { // type Email struct {
// Where string "attr" // Where string `xml:"attr"`
// Addr string // Addr string
// } // }
// //
// type Result struct { // type Result struct {
// XMLName xml.Name "result" // XMLName xml.Name `xml:"result"`
// Name string // Name string
// Phone string // Phone string
// Email []Email // Email []Email
// Groups []string "group>value" // Groups []string `xml:"group>value"`
// } // }
// //
// result := Result{Name: "name", Phone: "phone", Email: nil} // result := Result{Name: "name", Phone: "phone", Email: nil}
@ -79,11 +79,13 @@ import (
// Groups was assigned considering the element path provided in the // Groups was assigned considering the element path provided in the
// field tag. // field tag.
// //
// Because Unmarshal uses the reflect package, it can only // Because Unmarshal uses the reflect package, it can only assign
// assign to upper case fields. Unmarshal uses a case-insensitive // to exported (upper case) fields. Unmarshal uses a case-insensitive
// comparison to match XML element names to struct field names. // 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", // * If the struct has a field of type []byte or string with tag "innerxml",
// Unmarshal accumulates the raw XML nested inside the element // 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, // * If the struct has a field named XMLName of type xml.Name,
// Unmarshal records the element name in that field. // Unmarshal records the element name in that field.
// //
// * If the XMLName field has an associated tag string of the form // * If the XMLName field has an associated tag of the form
// "tag" or "namespace-URL tag", the XML element must have // "name" or "namespace-URL name", the XML element must have
// the given tag (and, optionally, name space) or else Unmarshal // the given name (and, optionally, name space) or else Unmarshal
// returns an error. // returns an error.
// //
// * If the XML element has an attribute whose name matches a // * If the XML element has an attribute whose name matches a
@ -112,14 +114,14 @@ import (
// field, the comments are discarded. // field, the comments are discarded.
// //
// * If the XML element contains a sub-element whose name matches // * 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 // will descend into the XML structure looking for elements with the
// given names, and will map the innermost elements to that struct field. // 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 ">". // with the field name followed by ">".
// //
// * If the XML element contains a sub-element whose name // * 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. // Unmarshal maps the sub-element to that struct field.
// Otherwise, if the struct has a field named Any, unmarshal // Otherwise, if the struct has a field named Any, unmarshal
// maps the sub-element to that struct field. // 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. // Assign name.
if f, ok := typ.FieldByName("XMLName"); ok { if f, ok := typ.FieldByName("XMLName"); ok {
// Validate element name. // Validate element name.
if f.Tag != "" { if tag := f.Tag.Get("xml"); tag != "" {
tag := f.Tag
ns := "" ns := ""
i := strings.LastIndex(tag, " ") i := strings.LastIndex(tag, " ")
if i >= 0 { 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. // Also, determine whether we need to save character data or comments.
for i, n := 0, typ.NumField(); i < n; i++ { for i, n := 0, typ.NumField(); i < n; i++ {
f := typ.Field(i) f := typ.Field(i)
switch f.Tag { switch f.Tag.Get("xml") {
case "attr": case "attr":
strv := sv.FieldByIndex(f.Index) strv := sv.FieldByIndex(f.Index)
// Look for attribute. // Look for attribute.
@ -366,15 +367,15 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error {
} }
default: default:
if strings.Contains(f.Tag, ">") { if tag := f.Tag.Get("xml"); strings.Contains(tag, ">") {
if fieldPaths == nil { if fieldPaths == nil {
fieldPaths = make(map[string]pathInfo) fieldPaths = make(map[string]pathInfo)
} }
path := strings.ToLower(f.Tag) path := strings.ToLower(tag)
if strings.HasPrefix(f.Tag, ">") { if strings.HasPrefix(tag, ">") {
path = strings.ToLower(f.Name) + path path = strings.ToLower(f.Name) + path
} }
if strings.HasSuffix(f.Tag, ">") { if strings.HasSuffix(tag, ">") {
path = path[:len(path)-1] path = path[:len(path)-1]
} }
err := addFieldPath(sv, fieldPaths, path, f.Index) 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() t := sv.Type()
f1 := t.FieldByIndex(idx1) f1 := t.FieldByIndex(idx1)
f2 := t.FieldByIndex(idx2) 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 // unmarshalPaths walks down an XML structure looking for

View File

@ -78,7 +78,7 @@ not being used from outside intra_region_diff.py.
</summary></entry></feed> ` </summary></entry></feed> `
type Feed struct { type Feed struct {
XMLName Name "http://www.w3.org/2005/Atom feed" XMLName Name `xml:"http://www.w3.org/2005/Atom feed"`
Title string Title string
Id string Id string
Link []Link Link []Link
@ -97,20 +97,20 @@ type Entry struct {
} }
type Link struct { type Link struct {
Rel string "attr" Rel string `xml:"attr"`
Href string "attr" Href string `xml:"attr"`
} }
type Person struct { type Person struct {
Name string Name string
URI string URI string
Email string Email string
InnerXML string "innerxml" InnerXML string `xml:"innerxml"`
} }
type Text struct { type Text struct {
Type string "attr" Type string `xml:"attr"`
Body string "chardata" Body string `xml:"chardata"`
} }
type Time string type Time string
@ -255,18 +255,18 @@ type PathTestItem struct {
} }
type PathTestA struct { type PathTestA struct {
Items []PathTestItem ">item1" Items []PathTestItem `xml:">item1"`
Before, After string Before, After string
} }
type PathTestB struct { type PathTestB struct {
Other []PathTestItem "items>Item1" Other []PathTestItem `xml:"items>Item1"`
Before, After string Before, After string
} }
type PathTestC struct { type PathTestC struct {
Values1 []string "items>item1>value" Values1 []string `xml:"items>item1>value"`
Values2 []string "items>item2>value" Values2 []string `xml:"items>item2>value"`
Before, After string Before, After string
} }
@ -275,7 +275,7 @@ type PathTestSet struct {
} }
type PathTestD struct { type PathTestD struct {
Other PathTestSet "items>" Other PathTestSet `xml:"items>"`
Before, After string Before, After string
} }
@ -299,15 +299,15 @@ func TestUnmarshalPaths(t *testing.T) {
} }
type BadPathTestA struct { type BadPathTestA struct {
First string "items>item1" First string `xml:"items>item1"`
Other string "items>item2" Other string `xml:"items>item2"`
Second string "items>" Second string `xml:"items>"`
} }
type BadPathTestB struct { type BadPathTestB struct {
Other string "items>item2>value" Other string `xml:"items>item2>value"`
First string "items>item1" First string `xml:"items>item1"`
Second string "items>item1>value" Second string `xml:"items>item1>value"`
} }
var badPathTests = []struct { var badPathTests = []struct {
@ -342,13 +342,13 @@ type AttrTest struct {
} }
type Test1 struct { type Test1 struct {
Int int "attr" Int int `xml:"attr"`
Float float64 "attr" Float float64 `xml:"attr"`
Uint8 uint8 "attr" Uint8 uint8 `xml:"attr"`
} }
type Test2 struct { type Test2 struct {
Bool bool "attr" Bool bool `xml:"attr"`
} }
const attrString = ` const attrString = `