1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939 |
- // Copyright 2011 The Go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- package xml
- import (
- "bytes"
- "errors"
- "fmt"
- "io"
- "reflect"
- "strconv"
- "strings"
- "sync"
- "testing"
- "time"
- )
- type DriveType int
- const (
- HyperDrive DriveType = iota
- ImprobabilityDrive
- )
- type Passenger struct {
- Name []string `xml:"name"`
- Weight float32 `xml:"weight"`
- }
- type Ship struct {
- XMLName struct{} `xml:"spaceship"`
- Name string `xml:"name,attr"`
- Pilot string `xml:"pilot,attr"`
- Drive DriveType `xml:"drive"`
- Age uint `xml:"age"`
- Passenger []*Passenger `xml:"passenger"`
- secret string
- }
- type NamedType string
- type Port struct {
- XMLName struct{} `xml:"port"`
- Type string `xml:"type,attr,omitempty"`
- Comment string `xml:",comment"`
- Number string `xml:",chardata"`
- }
- type Domain struct {
- XMLName struct{} `xml:"domain"`
- Country string `xml:",attr,omitempty"`
- Name []byte `xml:",chardata"`
- Comment []byte `xml:",comment"`
- }
- type Book struct {
- XMLName struct{} `xml:"book"`
- Title string `xml:",chardata"`
- }
- type Event struct {
- XMLName struct{} `xml:"event"`
- Year int `xml:",chardata"`
- }
- type Movie struct {
- XMLName struct{} `xml:"movie"`
- Length uint `xml:",chardata"`
- }
- type Pi struct {
- XMLName struct{} `xml:"pi"`
- Approximation float32 `xml:",chardata"`
- }
- type Universe struct {
- XMLName struct{} `xml:"universe"`
- Visible float64 `xml:",chardata"`
- }
- type Particle struct {
- XMLName struct{} `xml:"particle"`
- HasMass bool `xml:",chardata"`
- }
- type Departure struct {
- XMLName struct{} `xml:"departure"`
- When time.Time `xml:",chardata"`
- }
- type SecretAgent struct {
- XMLName struct{} `xml:"agent"`
- Handle string `xml:"handle,attr"`
- Identity string
- Obfuscate string `xml:",innerxml"`
- }
- type NestedItems struct {
- XMLName struct{} `xml:"result"`
- Items []string `xml:">item"`
- Item1 []string `xml:"Items>item1"`
- }
- type NestedOrder struct {
- XMLName struct{} `xml:"result"`
- Field1 string `xml:"parent>c"`
- Field2 string `xml:"parent>b"`
- Field3 string `xml:"parent>a"`
- }
- type MixedNested struct {
- XMLName struct{} `xml:"result"`
- A string `xml:"parent1>a"`
- B string `xml:"b"`
- C string `xml:"parent1>parent2>c"`
- D string `xml:"parent1>d"`
- }
- type NilTest struct {
- A interface{} `xml:"parent1>parent2>a"`
- B interface{} `xml:"parent1>b"`
- C interface{} `xml:"parent1>parent2>c"`
- }
- type Service struct {
- XMLName struct{} `xml:"service"`
- Domain *Domain `xml:"host>domain"`
- Port *Port `xml:"host>port"`
- Extra1 interface{}
- Extra2 interface{} `xml:"host>extra2"`
- }
- var nilStruct *Ship
- type EmbedA struct {
- EmbedC
- EmbedB EmbedB
- FieldA string
- }
- type EmbedB struct {
- FieldB string
- *EmbedC
- }
- type EmbedC struct {
- FieldA1 string `xml:"FieldA>A1"`
- FieldA2 string `xml:"FieldA>A2"`
- FieldB string
- FieldC string
- }
- type NameCasing struct {
- XMLName struct{} `xml:"casing"`
- Xy string
- XY string
- XyA string `xml:"Xy,attr"`
- XYA string `xml:"XY,attr"`
- }
- type NamePrecedence struct {
- XMLName Name `xml:"Parent"`
- FromTag XMLNameWithoutTag `xml:"InTag"`
- FromNameVal XMLNameWithoutTag
- FromNameTag XMLNameWithTag
- InFieldName string
- }
- type XMLNameWithTag struct {
- XMLName Name `xml:"InXMLNameTag"`
- Value string `xml:",chardata"`
- }
- type XMLNameWithNSTag struct {
- XMLName Name `xml:"ns InXMLNameWithNSTag"`
- Value string `xml:",chardata"`
- }
- type XMLNameWithoutTag struct {
- XMLName Name
- Value string `xml:",chardata"`
- }
- type NameInField struct {
- Foo Name `xml:"ns foo"`
- }
- type AttrTest struct {
- Int int `xml:",attr"`
- Named int `xml:"int,attr"`
- Float float64 `xml:",attr"`
- Uint8 uint8 `xml:",attr"`
- Bool bool `xml:",attr"`
- Str string `xml:",attr"`
- Bytes []byte `xml:",attr"`
- }
- type OmitAttrTest struct {
- Int int `xml:",attr,omitempty"`
- Named int `xml:"int,attr,omitempty"`
- Float float64 `xml:",attr,omitempty"`
- Uint8 uint8 `xml:",attr,omitempty"`
- Bool bool `xml:",attr,omitempty"`
- Str string `xml:",attr,omitempty"`
- Bytes []byte `xml:",attr,omitempty"`
- }
- type OmitFieldTest struct {
- Int int `xml:",omitempty"`
- Named int `xml:"int,omitempty"`
- Float float64 `xml:",omitempty"`
- Uint8 uint8 `xml:",omitempty"`
- Bool bool `xml:",omitempty"`
- Str string `xml:",omitempty"`
- Bytes []byte `xml:",omitempty"`
- Ptr *PresenceTest `xml:",omitempty"`
- }
- type AnyTest struct {
- XMLName struct{} `xml:"a"`
- Nested string `xml:"nested>value"`
- AnyField AnyHolder `xml:",any"`
- }
- type AnyOmitTest struct {
- XMLName struct{} `xml:"a"`
- Nested string `xml:"nested>value"`
- AnyField *AnyHolder `xml:",any,omitempty"`
- }
- type AnySliceTest struct {
- XMLName struct{} `xml:"a"`
- Nested string `xml:"nested>value"`
- AnyField []AnyHolder `xml:",any"`
- }
- type AnyHolder struct {
- XMLName Name
- XML string `xml:",innerxml"`
- }
- type RecurseA struct {
- A string
- B *RecurseB
- }
- type RecurseB struct {
- A *RecurseA
- B string
- }
- type PresenceTest struct {
- Exists *struct{}
- }
- type IgnoreTest struct {
- PublicSecret string `xml:"-"`
- }
- type MyBytes []byte
- type Data struct {
- Bytes []byte
- Attr []byte `xml:",attr"`
- Custom MyBytes
- }
- type Plain struct {
- V interface{}
- }
- type MyInt int
- type EmbedInt struct {
- MyInt
- }
- type Strings struct {
- X []string `xml:"A>B,omitempty"`
- }
- type PointerFieldsTest struct {
- XMLName Name `xml:"dummy"`
- Name *string `xml:"name,attr"`
- Age *uint `xml:"age,attr"`
- Empty *string `xml:"empty,attr"`
- Contents *string `xml:",chardata"`
- }
- type ChardataEmptyTest struct {
- XMLName Name `xml:"test"`
- Contents *string `xml:",chardata"`
- }
- type MyMarshalerTest struct {
- }
- var _ Marshaler = (*MyMarshalerTest)(nil)
- func (m *MyMarshalerTest) MarshalXML(e *Encoder, start StartElement) error {
- e.EncodeToken(start)
- e.EncodeToken(CharData([]byte("hello world")))
- e.EncodeToken(EndElement{start.Name})
- return nil
- }
- type MyMarshalerAttrTest struct{}
- var _ MarshalerAttr = (*MyMarshalerAttrTest)(nil)
- func (m *MyMarshalerAttrTest) MarshalXMLAttr(name Name) (Attr, error) {
- return Attr{name, "hello world"}, nil
- }
- type MyMarshalerValueAttrTest struct{}
- var _ MarshalerAttr = MyMarshalerValueAttrTest{}
- func (m MyMarshalerValueAttrTest) MarshalXMLAttr(name Name) (Attr, error) {
- return Attr{name, "hello world"}, nil
- }
- type MarshalerStruct struct {
- Foo MyMarshalerAttrTest `xml:",attr"`
- }
- type MarshalerValueStruct struct {
- Foo MyMarshalerValueAttrTest `xml:",attr"`
- }
- type InnerStruct struct {
- XMLName Name `xml:"testns outer"`
- }
- type OuterStruct struct {
- InnerStruct
- IntAttr int `xml:"int,attr"`
- }
- type OuterNamedStruct struct {
- InnerStruct
- XMLName Name `xml:"outerns test"`
- IntAttr int `xml:"int,attr"`
- }
- type OuterNamedOrderedStruct struct {
- XMLName Name `xml:"outerns test"`
- InnerStruct
- IntAttr int `xml:"int,attr"`
- }
- type OuterOuterStruct struct {
- OuterStruct
- }
- type NestedAndChardata struct {
- AB []string `xml:"A>B"`
- Chardata string `xml:",chardata"`
- }
- type NestedAndComment struct {
- AB []string `xml:"A>B"`
- Comment string `xml:",comment"`
- }
- type XMLNSFieldStruct struct {
- Ns string `xml:"xmlns,attr"`
- Body string
- }
- type NamedXMLNSFieldStruct struct {
- XMLName struct{} `xml:"testns test"`
- Ns string `xml:"xmlns,attr"`
- Body string
- }
- type XMLNSFieldStructWithOmitEmpty struct {
- Ns string `xml:"xmlns,attr,omitempty"`
- Body string
- }
- type NamedXMLNSFieldStructWithEmptyNamespace struct {
- XMLName struct{} `xml:"test"`
- Ns string `xml:"xmlns,attr"`
- Body string
- }
- type RecursiveXMLNSFieldStruct struct {
- Ns string `xml:"xmlns,attr"`
- Body *RecursiveXMLNSFieldStruct `xml:",omitempty"`
- Text string `xml:",omitempty"`
- }
- func ifaceptr(x interface{}) interface{} {
- return &x
- }
- var (
- nameAttr = "Sarah"
- ageAttr = uint(12)
- contentsAttr = "lorem ipsum"
- )
- // Unless explicitly stated as such (or *Plain), all of the
- // tests below are two-way tests. When introducing new tests,
- // please try to make them two-way as well to ensure that
- // marshalling and unmarshalling are as symmetrical as feasible.
- var marshalTests = []struct {
- Value interface{}
- ExpectXML string
- MarshalOnly bool
- UnmarshalOnly bool
- }{
- // Test nil marshals to nothing
- {Value: nil, ExpectXML: ``, MarshalOnly: true},
- {Value: nilStruct, ExpectXML: ``, MarshalOnly: true},
- // Test value types
- {Value: &Plain{true}, ExpectXML: `<Plain><V>true</V></Plain>`},
- {Value: &Plain{false}, ExpectXML: `<Plain><V>false</V></Plain>`},
- {Value: &Plain{int(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
- {Value: &Plain{int8(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
- {Value: &Plain{int16(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
- {Value: &Plain{int32(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
- {Value: &Plain{uint(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
- {Value: &Plain{uint8(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
- {Value: &Plain{uint16(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
- {Value: &Plain{uint32(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
- {Value: &Plain{float32(1.25)}, ExpectXML: `<Plain><V>1.25</V></Plain>`},
- {Value: &Plain{float64(1.25)}, ExpectXML: `<Plain><V>1.25</V></Plain>`},
- {Value: &Plain{uintptr(0xFFDD)}, ExpectXML: `<Plain><V>65501</V></Plain>`},
- {Value: &Plain{"gopher"}, ExpectXML: `<Plain><V>gopher</V></Plain>`},
- {Value: &Plain{[]byte("gopher")}, ExpectXML: `<Plain><V>gopher</V></Plain>`},
- {Value: &Plain{"</>"}, ExpectXML: `<Plain><V></></V></Plain>`},
- {Value: &Plain{[]byte("</>")}, ExpectXML: `<Plain><V></></V></Plain>`},
- {Value: &Plain{[3]byte{'<', '/', '>'}}, ExpectXML: `<Plain><V></></V></Plain>`},
- {Value: &Plain{NamedType("potato")}, ExpectXML: `<Plain><V>potato</V></Plain>`},
- {Value: &Plain{[]int{1, 2, 3}}, ExpectXML: `<Plain><V>1</V><V>2</V><V>3</V></Plain>`},
- {Value: &Plain{[3]int{1, 2, 3}}, ExpectXML: `<Plain><V>1</V><V>2</V><V>3</V></Plain>`},
- {Value: ifaceptr(true), MarshalOnly: true, ExpectXML: `<bool>true</bool>`},
- // Test time.
- {
- Value: &Plain{time.Unix(1e9, 123456789).UTC()},
- ExpectXML: `<Plain><V>2001-09-09T01:46:40.123456789Z</V></Plain>`,
- },
- // A pointer to struct{} may be used to test for an element's presence.
- {
- Value: &PresenceTest{new(struct{})},
- ExpectXML: `<PresenceTest><Exists></Exists></PresenceTest>`,
- },
- {
- Value: &PresenceTest{},
- ExpectXML: `<PresenceTest></PresenceTest>`,
- },
- // A pointer to struct{} may be used to test for an element's presence.
- {
- Value: &PresenceTest{new(struct{})},
- ExpectXML: `<PresenceTest><Exists></Exists></PresenceTest>`,
- },
- {
- Value: &PresenceTest{},
- ExpectXML: `<PresenceTest></PresenceTest>`,
- },
- // A []byte field is only nil if the element was not found.
- {
- Value: &Data{},
- ExpectXML: `<Data></Data>`,
- UnmarshalOnly: true,
- },
- {
- Value: &Data{Bytes: []byte{}, Custom: MyBytes{}, Attr: []byte{}},
- ExpectXML: `<Data Attr=""><Bytes></Bytes><Custom></Custom></Data>`,
- UnmarshalOnly: true,
- },
- // Check that []byte works, including named []byte types.
- {
- Value: &Data{Bytes: []byte("ab"), Custom: MyBytes("cd"), Attr: []byte{'v'}},
- ExpectXML: `<Data Attr="v"><Bytes>ab</Bytes><Custom>cd</Custom></Data>`,
- },
- // Test innerxml
- {
- Value: &SecretAgent{
- Handle: "007",
- Identity: "James Bond",
- Obfuscate: "<redacted/>",
- },
- ExpectXML: `<agent handle="007"><Identity>James Bond</Identity><redacted/></agent>`,
- MarshalOnly: true,
- },
- {
- Value: &SecretAgent{
- Handle: "007",
- Identity: "James Bond",
- Obfuscate: "<Identity>James Bond</Identity><redacted/>",
- },
- ExpectXML: `<agent handle="007"><Identity>James Bond</Identity><redacted/></agent>`,
- UnmarshalOnly: true,
- },
- // Test structs
- {Value: &Port{Type: "ssl", Number: "443"}, ExpectXML: `<port type="ssl">443</port>`},
- {Value: &Port{Number: "443"}, ExpectXML: `<port>443</port>`},
- {Value: &Port{Type: "<unix>"}, ExpectXML: `<port type="<unix>"></port>`},
- {Value: &Port{Number: "443", Comment: "https"}, ExpectXML: `<port><!--https-->443</port>`},
- {Value: &Port{Number: "443", Comment: "add space-"}, ExpectXML: `<port><!--add space- -->443</port>`, MarshalOnly: true},
- {Value: &Domain{Name: []byte("google.com&friends")}, ExpectXML: `<domain>google.com&friends</domain>`},
- {Value: &Domain{Name: []byte("google.com"), Comment: []byte(" &friends ")}, ExpectXML: `<domain>google.com<!-- &friends --></domain>`},
- {Value: &Book{Title: "Pride & Prejudice"}, ExpectXML: `<book>Pride & Prejudice</book>`},
- {Value: &Event{Year: -3114}, ExpectXML: `<event>-3114</event>`},
- {Value: &Movie{Length: 13440}, ExpectXML: `<movie>13440</movie>`},
- {Value: &Pi{Approximation: 3.14159265}, ExpectXML: `<pi>3.1415927</pi>`},
- {Value: &Universe{Visible: 9.3e13}, ExpectXML: `<universe>9.3e+13</universe>`},
- {Value: &Particle{HasMass: true}, ExpectXML: `<particle>true</particle>`},
- {Value: &Departure{When: ParseTime("2013-01-09T00:15:00-09:00")}, ExpectXML: `<departure>2013-01-09T00:15:00-09:00</departure>`},
- {Value: atomValue, ExpectXML: atomXml},
- {
- Value: &Ship{
- Name: "Heart of Gold",
- Pilot: "Computer",
- Age: 1,
- Drive: ImprobabilityDrive,
- Passenger: []*Passenger{
- {
- Name: []string{"Zaphod", "Beeblebrox"},
- Weight: 7.25,
- },
- {
- Name: []string{"Trisha", "McMillen"},
- Weight: 5.5,
- },
- {
- Name: []string{"Ford", "Prefect"},
- Weight: 7,
- },
- {
- Name: []string{"Arthur", "Dent"},
- Weight: 6.75,
- },
- },
- },
- ExpectXML: `<spaceship name="Heart of Gold" pilot="Computer">` +
- `<drive>` + strconv.Itoa(int(ImprobabilityDrive)) + `</drive>` +
- `<age>1</age>` +
- `<passenger>` +
- `<name>Zaphod</name>` +
- `<name>Beeblebrox</name>` +
- `<weight>7.25</weight>` +
- `</passenger>` +
- `<passenger>` +
- `<name>Trisha</name>` +
- `<name>McMillen</name>` +
- `<weight>5.5</weight>` +
- `</passenger>` +
- `<passenger>` +
- `<name>Ford</name>` +
- `<name>Prefect</name>` +
- `<weight>7</weight>` +
- `</passenger>` +
- `<passenger>` +
- `<name>Arthur</name>` +
- `<name>Dent</name>` +
- `<weight>6.75</weight>` +
- `</passenger>` +
- `</spaceship>`,
- },
- // Test a>b
- {
- Value: &NestedItems{Items: nil, Item1: nil},
- ExpectXML: `<result>` +
- `<Items>` +
- `</Items>` +
- `</result>`,
- },
- {
- Value: &NestedItems{Items: []string{}, Item1: []string{}},
- ExpectXML: `<result>` +
- `<Items>` +
- `</Items>` +
- `</result>`,
- MarshalOnly: true,
- },
- {
- Value: &NestedItems{Items: nil, Item1: []string{"A"}},
- ExpectXML: `<result>` +
- `<Items>` +
- `<item1>A</item1>` +
- `</Items>` +
- `</result>`,
- },
- {
- Value: &NestedItems{Items: []string{"A", "B"}, Item1: nil},
- ExpectXML: `<result>` +
- `<Items>` +
- `<item>A</item>` +
- `<item>B</item>` +
- `</Items>` +
- `</result>`,
- },
- {
- Value: &NestedItems{Items: []string{"A", "B"}, Item1: []string{"C"}},
- ExpectXML: `<result>` +
- `<Items>` +
- `<item>A</item>` +
- `<item>B</item>` +
- `<item1>C</item1>` +
- `</Items>` +
- `</result>`,
- },
- {
- Value: &NestedOrder{Field1: "C", Field2: "B", Field3: "A"},
- ExpectXML: `<result>` +
- `<parent>` +
- `<c>C</c>` +
- `<b>B</b>` +
- `<a>A</a>` +
- `</parent>` +
- `</result>`,
- },
- {
- Value: &NilTest{A: "A", B: nil, C: "C"},
- ExpectXML: `<NilTest>` +
- `<parent1>` +
- `<parent2><a>A</a></parent2>` +
- `<parent2><c>C</c></parent2>` +
- `</parent1>` +
- `</NilTest>`,
- MarshalOnly: true, // Uses interface{}
- },
- {
- Value: &MixedNested{A: "A", B: "B", C: "C", D: "D"},
- ExpectXML: `<result>` +
- `<parent1><a>A</a></parent1>` +
- `<b>B</b>` +
- `<parent1>` +
- `<parent2><c>C</c></parent2>` +
- `<d>D</d>` +
- `</parent1>` +
- `</result>`,
- },
- {
- Value: &Service{Port: &Port{Number: "80"}},
- ExpectXML: `<service><host><port>80</port></host></service>`,
- },
- {
- Value: &Service{},
- ExpectXML: `<service></service>`,
- },
- {
- Value: &Service{Port: &Port{Number: "80"}, Extra1: "A", Extra2: "B"},
- ExpectXML: `<service>` +
- `<host><port>80</port></host>` +
- `<Extra1>A</Extra1>` +
- `<host><extra2>B</extra2></host>` +
- `</service>`,
- MarshalOnly: true,
- },
- {
- Value: &Service{Port: &Port{Number: "80"}, Extra2: "example"},
- ExpectXML: `<service>` +
- `<host><port>80</port></host>` +
- `<host><extra2>example</extra2></host>` +
- `</service>`,
- MarshalOnly: true,
- },
- {
- Value: &struct {
- XMLName struct{} `xml:"space top"`
- A string `xml:"x>a"`
- B string `xml:"x>b"`
- C string `xml:"space x>c"`
- C1 string `xml:"space1 x>c"`
- D1 string `xml:"space1 x>d"`
- E1 string `xml:"x>e"`
- }{
- A: "a",
- B: "b",
- C: "c",
- C1: "c1",
- D1: "d1",
- E1: "e1",
- },
- ExpectXML: `<top xmlns="space">` +
- `<x><a>a</a><b>b</b><c>c</c></x>` +
- `<x xmlns="space1">` +
- `<c>c1</c>` +
- `<d>d1</d>` +
- `</x>` +
- `<x>` +
- `<e>e1</e>` +
- `</x>` +
- `</top>`,
- },
- {
- Value: &struct {
- XMLName Name
- A string `xml:"x>a"`
- B string `xml:"x>b"`
- C string `xml:"space x>c"`
- C1 string `xml:"space1 x>c"`
- D1 string `xml:"space1 x>d"`
- }{
- XMLName: Name{
- Space: "space0",
- Local: "top",
- },
- A: "a",
- B: "b",
- C: "c",
- C1: "c1",
- D1: "d1",
- },
- ExpectXML: `<top xmlns="space0">` +
- `<x><a>a</a><b>b</b></x>` +
- `<x xmlns="space"><c>c</c></x>` +
- `<x xmlns="space1">` +
- `<c>c1</c>` +
- `<d>d1</d>` +
- `</x>` +
- `</top>`,
- },
- {
- Value: &struct {
- XMLName struct{} `xml:"top"`
- B string `xml:"space x>b"`
- B1 string `xml:"space1 x>b"`
- }{
- B: "b",
- B1: "b1",
- },
- ExpectXML: `<top>` +
- `<x xmlns="space"><b>b</b></x>` +
- `<x xmlns="space1"><b>b1</b></x>` +
- `</top>`,
- },
- // Test struct embedding
- {
- Value: &EmbedA{
- EmbedC: EmbedC{
- FieldA1: "", // Shadowed by A.A
- FieldA2: "", // Shadowed by A.A
- FieldB: "A.C.B",
- FieldC: "A.C.C",
- },
- EmbedB: EmbedB{
- FieldB: "A.B.B",
- EmbedC: &EmbedC{
- FieldA1: "A.B.C.A1",
- FieldA2: "A.B.C.A2",
- FieldB: "", // Shadowed by A.B.B
- FieldC: "A.B.C.C",
- },
- },
- FieldA: "A.A",
- },
- ExpectXML: `<EmbedA>` +
- `<FieldB>A.C.B</FieldB>` +
- `<FieldC>A.C.C</FieldC>` +
- `<EmbedB>` +
- `<FieldB>A.B.B</FieldB>` +
- `<FieldA>` +
- `<A1>A.B.C.A1</A1>` +
- `<A2>A.B.C.A2</A2>` +
- `</FieldA>` +
- `<FieldC>A.B.C.C</FieldC>` +
- `</EmbedB>` +
- `<FieldA>A.A</FieldA>` +
- `</EmbedA>`,
- },
- // Test that name casing matters
- {
- Value: &NameCasing{Xy: "mixed", XY: "upper", XyA: "mixedA", XYA: "upperA"},
- ExpectXML: `<casing Xy="mixedA" XY="upperA"><Xy>mixed</Xy><XY>upper</XY></casing>`,
- },
- // Test the order in which the XML element name is chosen
- {
- Value: &NamePrecedence{
- FromTag: XMLNameWithoutTag{Value: "A"},
- FromNameVal: XMLNameWithoutTag{XMLName: Name{Local: "InXMLName"}, Value: "B"},
- FromNameTag: XMLNameWithTag{Value: "C"},
- InFieldName: "D",
- },
- ExpectXML: `<Parent>` +
- `<InTag>A</InTag>` +
- `<InXMLName>B</InXMLName>` +
- `<InXMLNameTag>C</InXMLNameTag>` +
- `<InFieldName>D</InFieldName>` +
- `</Parent>`,
- MarshalOnly: true,
- },
- {
- Value: &NamePrecedence{
- XMLName: Name{Local: "Parent"},
- FromTag: XMLNameWithoutTag{XMLName: Name{Local: "InTag"}, Value: "A"},
- FromNameVal: XMLNameWithoutTag{XMLName: Name{Local: "FromNameVal"}, Value: "B"},
- FromNameTag: XMLNameWithTag{XMLName: Name{Local: "InXMLNameTag"}, Value: "C"},
- InFieldName: "D",
- },
- ExpectXML: `<Parent>` +
- `<InTag>A</InTag>` +
- `<FromNameVal>B</FromNameVal>` +
- `<InXMLNameTag>C</InXMLNameTag>` +
- `<InFieldName>D</InFieldName>` +
- `</Parent>`,
- UnmarshalOnly: true,
- },
- // xml.Name works in a plain field as well.
- {
- Value: &NameInField{Name{Space: "ns", Local: "foo"}},
- ExpectXML: `<NameInField><foo xmlns="ns"></foo></NameInField>`,
- },
- {
- Value: &NameInField{Name{Space: "ns", Local: "foo"}},
- ExpectXML: `<NameInField><foo xmlns="ns"><ignore></ignore></foo></NameInField>`,
- UnmarshalOnly: true,
- },
- // Marshaling zero xml.Name uses the tag or field name.
- {
- Value: &NameInField{},
- ExpectXML: `<NameInField><foo xmlns="ns"></foo></NameInField>`,
- MarshalOnly: true,
- },
- // Test attributes
- {
- Value: &AttrTest{
- Int: 8,
- Named: 9,
- Float: 23.5,
- Uint8: 255,
- Bool: true,
- Str: "str",
- Bytes: []byte("byt"),
- },
- ExpectXML: `<AttrTest Int="8" int="9" Float="23.5" Uint8="255"` +
- ` Bool="true" Str="str" Bytes="byt"></AttrTest>`,
- },
- {
- Value: &AttrTest{Bytes: []byte{}},
- ExpectXML: `<AttrTest Int="0" int="0" Float="0" Uint8="0"` +
- ` Bool="false" Str="" Bytes=""></AttrTest>`,
- },
- {
- Value: &OmitAttrTest{
- Int: 8,
- Named: 9,
- Float: 23.5,
- Uint8: 255,
- Bool: true,
- Str: "str",
- Bytes: []byte("byt"),
- },
- ExpectXML: `<OmitAttrTest Int="8" int="9" Float="23.5" Uint8="255"` +
- ` Bool="true" Str="str" Bytes="byt"></OmitAttrTest>`,
- },
- {
- Value: &OmitAttrTest{},
- ExpectXML: `<OmitAttrTest></OmitAttrTest>`,
- },
- // pointer fields
- {
- Value: &PointerFieldsTest{Name: &nameAttr, Age: &ageAttr, Contents: &contentsAttr},
- ExpectXML: `<dummy name="Sarah" age="12">lorem ipsum</dummy>`,
- MarshalOnly: true,
- },
- // empty chardata pointer field
- {
- Value: &ChardataEmptyTest{},
- ExpectXML: `<test></test>`,
- MarshalOnly: true,
- },
- // omitempty on fields
- {
- Value: &OmitFieldTest{
- Int: 8,
- Named: 9,
- Float: 23.5,
- Uint8: 255,
- Bool: true,
- Str: "str",
- Bytes: []byte("byt"),
- Ptr: &PresenceTest{},
- },
- ExpectXML: `<OmitFieldTest>` +
- `<Int>8</Int>` +
- `<int>9</int>` +
- `<Float>23.5</Float>` +
- `<Uint8>255</Uint8>` +
- `<Bool>true</Bool>` +
- `<Str>str</Str>` +
- `<Bytes>byt</Bytes>` +
- `<Ptr></Ptr>` +
- `</OmitFieldTest>`,
- },
- {
- Value: &OmitFieldTest{},
- ExpectXML: `<OmitFieldTest></OmitFieldTest>`,
- },
- // Test ",any"
- {
- ExpectXML: `<a><nested><value>known</value></nested><other><sub>unknown</sub></other></a>`,
- Value: &AnyTest{
- Nested: "known",
- AnyField: AnyHolder{
- XMLName: Name{Local: "other"},
- XML: "<sub>unknown</sub>",
- },
- },
- },
- {
- Value: &AnyTest{Nested: "known",
- AnyField: AnyHolder{
- XML: "<unknown/>",
- XMLName: Name{Local: "AnyField"},
- },
- },
- ExpectXML: `<a><nested><value>known</value></nested><AnyField><unknown/></AnyField></a>`,
- },
- {
- ExpectXML: `<a><nested><value>b</value></nested></a>`,
- Value: &AnyOmitTest{
- Nested: "b",
- },
- },
- {
- ExpectXML: `<a><nested><value>b</value></nested><c><d>e</d></c><g xmlns="f"><h>i</h></g></a>`,
- Value: &AnySliceTest{
- Nested: "b",
- AnyField: []AnyHolder{
- {
- XMLName: Name{Local: "c"},
- XML: "<d>e</d>",
- },
- {
- XMLName: Name{Space: "f", Local: "g"},
- XML: "<h>i</h>",
- },
- },
- },
- },
- {
- ExpectXML: `<a><nested><value>b</value></nested></a>`,
- Value: &AnySliceTest{
- Nested: "b",
- },
- },
- // Test recursive types.
- {
- Value: &RecurseA{
- A: "a1",
- B: &RecurseB{
- A: &RecurseA{"a2", nil},
- B: "b1",
- },
- },
- ExpectXML: `<RecurseA><A>a1</A><B><A><A>a2</A></A><B>b1</B></B></RecurseA>`,
- },
- // Test ignoring fields via "-" tag
- {
- ExpectXML: `<IgnoreTest></IgnoreTest>`,
- Value: &IgnoreTest{},
- },
- {
- ExpectXML: `<IgnoreTest></IgnoreTest>`,
- Value: &IgnoreTest{PublicSecret: "can't tell"},
- MarshalOnly: true,
- },
- {
- ExpectXML: `<IgnoreTest><PublicSecret>ignore me</PublicSecret></IgnoreTest>`,
- Value: &IgnoreTest{},
- UnmarshalOnly: true,
- },
- // Test escaping.
- {
- ExpectXML: `<a><nested><value>dquote: "; squote: '; ampersand: &; less: <; greater: >;</value></nested><empty></empty></a>`,
- Value: &AnyTest{
- Nested: `dquote: "; squote: '; ampersand: &; less: <; greater: >;`,
- AnyField: AnyHolder{XMLName: Name{Local: "empty"}},
- },
- },
- {
- ExpectXML: `<a><nested><value>newline: 
; cr: 
; tab: 	;</value></nested><AnyField></AnyField></a>`,
- Value: &AnyTest{
- Nested: "newline: \n; cr: \r; tab: \t;",
- AnyField: AnyHolder{XMLName: Name{Local: "AnyField"}},
- },
- },
- {
- ExpectXML: "<a><nested><value>1\r2\r\n3\n\r4\n5</value></nested></a>",
- Value: &AnyTest{
- Nested: "1\n2\n3\n\n4\n5",
- },
- UnmarshalOnly: true,
- },
- {
- ExpectXML: `<EmbedInt><MyInt>42</MyInt></EmbedInt>`,
- Value: &EmbedInt{
- MyInt: 42,
- },
- },
- // Test omitempty with parent chain; see golang.org/issue/4168.
- {
- ExpectXML: `<Strings><A></A></Strings>`,
- Value: &Strings{},
- },
- // Custom marshalers.
- {
- ExpectXML: `<MyMarshalerTest>hello world</MyMarshalerTest>`,
- Value: &MyMarshalerTest{},
- },
- {
- ExpectXML: `<MarshalerStruct Foo="hello world"></MarshalerStruct>`,
- Value: &MarshalerStruct{},
- },
- {
- ExpectXML: `<MarshalerValueStruct Foo="hello world"></MarshalerValueStruct>`,
- Value: &MarshalerValueStruct{},
- },
- {
- ExpectXML: `<outer xmlns="testns" int="10"></outer>`,
- Value: &OuterStruct{IntAttr: 10},
- },
- {
- ExpectXML: `<test xmlns="outerns" int="10"></test>`,
- Value: &OuterNamedStruct{XMLName: Name{Space: "outerns", Local: "test"}, IntAttr: 10},
- },
- {
- ExpectXML: `<test xmlns="outerns" int="10"></test>`,
- Value: &OuterNamedOrderedStruct{XMLName: Name{Space: "outerns", Local: "test"}, IntAttr: 10},
- },
- {
- ExpectXML: `<outer xmlns="testns" int="10"></outer>`,
- Value: &OuterOuterStruct{OuterStruct{IntAttr: 10}},
- },
- {
- ExpectXML: `<NestedAndChardata><A><B></B><B></B></A>test</NestedAndChardata>`,
- Value: &NestedAndChardata{AB: make([]string, 2), Chardata: "test"},
- },
- {
- ExpectXML: `<NestedAndComment><A><B></B><B></B></A><!--test--></NestedAndComment>`,
- Value: &NestedAndComment{AB: make([]string, 2), Comment: "test"},
- },
- {
- ExpectXML: `<XMLNSFieldStruct xmlns="http://example.com/ns"><Body>hello world</Body></XMLNSFieldStruct>`,
- Value: &XMLNSFieldStruct{Ns: "http://example.com/ns", Body: "hello world"},
- },
- {
- ExpectXML: `<testns:test xmlns:testns="testns" xmlns="http://example.com/ns"><Body>hello world</Body></testns:test>`,
- Value: &NamedXMLNSFieldStruct{Ns: "http://example.com/ns", Body: "hello world"},
- },
- {
- ExpectXML: `<testns:test xmlns:testns="testns"><Body>hello world</Body></testns:test>`,
- Value: &NamedXMLNSFieldStruct{Ns: "", Body: "hello world"},
- },
- {
- ExpectXML: `<XMLNSFieldStructWithOmitEmpty><Body>hello world</Body></XMLNSFieldStructWithOmitEmpty>`,
- Value: &XMLNSFieldStructWithOmitEmpty{Body: "hello world"},
- },
- {
- // The xmlns attribute must be ignored because the <test>
- // element is in the empty namespace, so it's not possible
- // to set the default namespace to something non-empty.
- ExpectXML: `<test><Body>hello world</Body></test>`,
- Value: &NamedXMLNSFieldStructWithEmptyNamespace{Ns: "foo", Body: "hello world"},
- MarshalOnly: true,
- },
- {
- ExpectXML: `<RecursiveXMLNSFieldStruct xmlns="foo"><Body xmlns=""><Text>hello world</Text></Body></RecursiveXMLNSFieldStruct>`,
- Value: &RecursiveXMLNSFieldStruct{
- Ns: "foo",
- Body: &RecursiveXMLNSFieldStruct{
- Text: "hello world",
- },
- },
- },
- }
- func TestMarshal(t *testing.T) {
- for idx, test := range marshalTests {
- if test.UnmarshalOnly {
- continue
- }
- data, err := Marshal(test.Value)
- if err != nil {
- t.Errorf("#%d: marshal(%#v): %s", idx, test.Value, err)
- continue
- }
- if got, want := string(data), test.ExpectXML; got != want {
- if strings.Contains(want, "\n") {
- t.Errorf("#%d: marshal(%#v):\nHAVE:\n%s\nWANT:\n%s", idx, test.Value, got, want)
- } else {
- t.Errorf("#%d: marshal(%#v):\nhave %#q\nwant %#q", idx, test.Value, got, want)
- }
- }
- }
- }
- type AttrParent struct {
- X string `xml:"X>Y,attr"`
- }
- type BadAttr struct {
- Name []string `xml:"name,attr"`
- }
- var marshalErrorTests = []struct {
- Value interface{}
- Err string
- Kind reflect.Kind
- }{
- {
- Value: make(chan bool),
- Err: "xml: unsupported type: chan bool",
- Kind: reflect.Chan,
- },
- {
- Value: map[string]string{
- "question": "What do you get when you multiply six by nine?",
- "answer": "42",
- },
- Err: "xml: unsupported type: map[string]string",
- Kind: reflect.Map,
- },
- {
- Value: map[*Ship]bool{nil: false},
- Err: "xml: unsupported type: map[*xml.Ship]bool",
- Kind: reflect.Map,
- },
- {
- Value: &Domain{Comment: []byte("f--bar")},
- Err: `xml: comments must not contain "--"`,
- },
- // Reject parent chain with attr, never worked; see golang.org/issue/5033.
- {
- Value: &AttrParent{},
- Err: `xml: X>Y chain not valid with attr flag`,
- },
- {
- Value: BadAttr{[]string{"X", "Y"}},
- Err: `xml: unsupported type: []string`,
- },
- }
- var marshalIndentTests = []struct {
- Value interface{}
- Prefix string
- Indent string
- ExpectXML string
- }{
- {
- Value: &SecretAgent{
- Handle: "007",
- Identity: "James Bond",
- Obfuscate: "<redacted/>",
- },
- Prefix: "",
- Indent: "\t",
- ExpectXML: fmt.Sprintf("<agent handle=\"007\">\n\t<Identity>James Bond</Identity><redacted/>\n</agent>"),
- },
- }
- func TestMarshalErrors(t *testing.T) {
- for idx, test := range marshalErrorTests {
- data, err := Marshal(test.Value)
- if err == nil {
- t.Errorf("#%d: marshal(%#v) = [success] %q, want error %v", idx, test.Value, data, test.Err)
- continue
- }
- if err.Error() != test.Err {
- t.Errorf("#%d: marshal(%#v) = [error] %v, want %v", idx, test.Value, err, test.Err)
- }
- if test.Kind != reflect.Invalid {
- if kind := err.(*UnsupportedTypeError).Type.Kind(); kind != test.Kind {
- t.Errorf("#%d: marshal(%#v) = [error kind] %s, want %s", idx, test.Value, kind, test.Kind)
- }
- }
- }
- }
- // Do invertibility testing on the various structures that we test
- func TestUnmarshal(t *testing.T) {
- for i, test := range marshalTests {
- if test.MarshalOnly {
- continue
- }
- if _, ok := test.Value.(*Plain); ok {
- continue
- }
- vt := reflect.TypeOf(test.Value)
- dest := reflect.New(vt.Elem()).Interface()
- err := Unmarshal([]byte(test.ExpectXML), dest)
- switch fix := dest.(type) {
- case *Feed:
- fix.Author.InnerXML = ""
- for i := range fix.Entry {
- fix.Entry[i].Author.InnerXML = ""
- }
- }
- if err != nil {
- t.Errorf("#%d: unexpected error: %#v", i, err)
- } else if got, want := dest, test.Value; !reflect.DeepEqual(got, want) {
- t.Errorf("#%d: unmarshal(%q):\nhave %#v\nwant %#v", i, test.ExpectXML, got, want)
- }
- }
- }
- func TestMarshalIndent(t *testing.T) {
- for i, test := range marshalIndentTests {
- data, err := MarshalIndent(test.Value, test.Prefix, test.Indent)
- if err != nil {
- t.Errorf("#%d: Error: %s", i, err)
- continue
- }
- if got, want := string(data), test.ExpectXML; got != want {
- t.Errorf("#%d: MarshalIndent:\nGot:%s\nWant:\n%s", i, got, want)
- }
- }
- }
- type limitedBytesWriter struct {
- w io.Writer
- remain int // until writes fail
- }
- func (lw *limitedBytesWriter) Write(p []byte) (n int, err error) {
- if lw.remain <= 0 {
- println("error")
- return 0, errors.New("write limit hit")
- }
- if len(p) > lw.remain {
- p = p[:lw.remain]
- n, _ = lw.w.Write(p)
- lw.remain = 0
- return n, errors.New("write limit hit")
- }
- n, err = lw.w.Write(p)
- lw.remain -= n
- return n, err
- }
- func TestMarshalWriteErrors(t *testing.T) {
- var buf bytes.Buffer
- const writeCap = 1024
- w := &limitedBytesWriter{&buf, writeCap}
- enc := NewEncoder(w)
- var err error
- var i int
- const n = 4000
- for i = 1; i <= n; i++ {
- err = enc.Encode(&Passenger{
- Name: []string{"Alice", "Bob"},
- Weight: 5,
- })
- if err != nil {
- break
- }
- }
- if err == nil {
- t.Error("expected an error")
- }
- if i == n {
- t.Errorf("expected to fail before the end")
- }
- if buf.Len() != writeCap {
- t.Errorf("buf.Len() = %d; want %d", buf.Len(), writeCap)
- }
- }
- func TestMarshalWriteIOErrors(t *testing.T) {
- enc := NewEncoder(errWriter{})
- expectErr := "unwritable"
- err := enc.Encode(&Passenger{})
- if err == nil || err.Error() != expectErr {
- t.Errorf("EscapeTest = [error] %v, want %v", err, expectErr)
- }
- }
- func TestMarshalFlush(t *testing.T) {
- var buf bytes.Buffer
- enc := NewEncoder(&buf)
- if err := enc.EncodeToken(CharData("hello world")); err != nil {
- t.Fatalf("enc.EncodeToken: %v", err)
- }
- if buf.Len() > 0 {
- t.Fatalf("enc.EncodeToken caused actual write: %q", buf.Bytes())
- }
- if err := enc.Flush(); err != nil {
- t.Fatalf("enc.Flush: %v", err)
- }
- if buf.String() != "hello world" {
- t.Fatalf("after enc.Flush, buf.String() = %q, want %q", buf.String(), "hello world")
- }
- }
- var encodeElementTests = []struct {
- desc string
- value interface{}
- start StartElement
- expectXML string
- }{{
- desc: "simple string",
- value: "hello",
- start: StartElement{
- Name: Name{Local: "a"},
- },
- expectXML: `<a>hello</a>`,
- }, {
- desc: "string with added attributes",
- value: "hello",
- start: StartElement{
- Name: Name{Local: "a"},
- Attr: []Attr{{
- Name: Name{Local: "x"},
- Value: "y",
- }, {
- Name: Name{Local: "foo"},
- Value: "bar",
- }},
- },
- expectXML: `<a x="y" foo="bar">hello</a>`,
- }, {
- desc: "start element with default name space",
- value: struct {
- Foo XMLNameWithNSTag
- }{
- Foo: XMLNameWithNSTag{
- Value: "hello",
- },
- },
- start: StartElement{
- Name: Name{Space: "ns", Local: "a"},
- Attr: []Attr{{
- Name: Name{Local: "xmlns"},
- // "ns" is the name space defined in XMLNameWithNSTag
- Value: "ns",
- }},
- },
- expectXML: `<a xmlns="ns"><InXMLNameWithNSTag>hello</InXMLNameWithNSTag></a>`,
- }, {
- desc: "start element in name space with different default name space",
- value: struct {
- Foo XMLNameWithNSTag
- }{
- Foo: XMLNameWithNSTag{
- Value: "hello",
- },
- },
- start: StartElement{
- Name: Name{Space: "ns2", Local: "a"},
- Attr: []Attr{{
- Name: Name{Local: "xmlns"},
- // "ns" is the name space defined in XMLNameWithNSTag
- Value: "ns",
- }},
- },
- expectXML: `<ns2:a xmlns:ns2="ns2" xmlns="ns"><InXMLNameWithNSTag>hello</InXMLNameWithNSTag></ns2:a>`,
- }, {
- desc: "XMLMarshaler with start element with default name space",
- value: &MyMarshalerTest{},
- start: StartElement{
- Name: Name{Space: "ns2", Local: "a"},
- Attr: []Attr{{
- Name: Name{Local: "xmlns"},
- // "ns" is the name space defined in XMLNameWithNSTag
- Value: "ns",
- }},
- },
- expectXML: `<ns2:a xmlns:ns2="ns2" xmlns="ns">hello world</ns2:a>`,
- }}
- func TestEncodeElement(t *testing.T) {
- for idx, test := range encodeElementTests {
- var buf bytes.Buffer
- enc := NewEncoder(&buf)
- err := enc.EncodeElement(test.value, test.start)
- if err != nil {
- t.Fatalf("enc.EncodeElement: %v", err)
- }
- err = enc.Flush()
- if err != nil {
- t.Fatalf("enc.Flush: %v", err)
- }
- if got, want := buf.String(), test.expectXML; got != want {
- t.Errorf("#%d(%s): EncodeElement(%#v, %#v):\nhave %#q\nwant %#q", idx, test.desc, test.value, test.start, got, want)
- }
- }
- }
- func BenchmarkMarshal(b *testing.B) {
- b.ReportAllocs()
- for i := 0; i < b.N; i++ {
- Marshal(atomValue)
- }
- }
- func BenchmarkUnmarshal(b *testing.B) {
- b.ReportAllocs()
- xml := []byte(atomXml)
- for i := 0; i < b.N; i++ {
- Unmarshal(xml, &Feed{})
- }
- }
- // golang.org/issue/6556
- func TestStructPointerMarshal(t *testing.T) {
- type A struct {
- XMLName string `xml:"a"`
- B []interface{}
- }
- type C struct {
- XMLName Name
- Value string `xml:"value"`
- }
- a := new(A)
- a.B = append(a.B, &C{
- XMLName: Name{Local: "c"},
- Value: "x",
- })
- b, err := Marshal(a)
- if err != nil {
- t.Fatal(err)
- }
- if x := string(b); x != "<a><c><value>x</value></c></a>" {
- t.Fatal(x)
- }
- var v A
- err = Unmarshal(b, &v)
- if err != nil {
- t.Fatal(err)
- }
- }
- var encodeTokenTests = []struct {
- desc string
- toks []Token
- want string
- err string
- }{{
- desc: "start element with name space",
- toks: []Token{
- StartElement{Name{"space", "local"}, nil},
- },
- want: `<space:local xmlns:space="space">`,
- }, {
- desc: "start element with no name",
- toks: []Token{
- StartElement{Name{"space", ""}, nil},
- },
- err: "xml: start tag with no name",
- }, {
- desc: "end element with no name",
- toks: []Token{
- EndElement{Name{"space", ""}},
- },
- err: "xml: end tag with no name",
- }, {
- desc: "char data",
- toks: []Token{
- CharData("foo"),
- },
- want: `foo`,
- }, {
- desc: "char data with escaped chars",
- toks: []Token{
- CharData(" \t\n"),
- },
- want: " 	\n",
- }, {
- desc: "comment",
- toks: []Token{
- Comment("foo"),
- },
- want: `<!--foo-->`,
- }, {
- desc: "comment with invalid content",
- toks: []Token{
- Comment("foo-->"),
- },
- err: "xml: EncodeToken of Comment containing --> marker",
- }, {
- desc: "proc instruction",
- toks: []Token{
- ProcInst{"Target", []byte("Instruction")},
- },
- want: `<?Target Instruction?>`,
- }, {
- desc: "proc instruction with empty target",
- toks: []Token{
- ProcInst{"", []byte("Instruction")},
- },
- err: "xml: EncodeToken of ProcInst with invalid Target",
- }, {
- desc: "proc instruction with bad content",
- toks: []Token{
- ProcInst{"", []byte("Instruction?>")},
- },
- err: "xml: EncodeToken of ProcInst with invalid Target",
- }, {
- desc: "directive",
- toks: []Token{
- Directive("foo"),
- },
- want: `<!foo>`,
- }, {
- desc: "more complex directive",
- toks: []Token{
- Directive("DOCTYPE doc [ <!ELEMENT doc '>'> <!-- com>ment --> ]"),
- },
- want: `<!DOCTYPE doc [ <!ELEMENT doc '>'> <!-- com>ment --> ]>`,
- }, {
- desc: "directive instruction with bad name",
- toks: []Token{
- Directive("foo>"),
- },
- err: "xml: EncodeToken of Directive containing wrong < or > markers",
- }, {
- desc: "end tag without start tag",
- toks: []Token{
- EndElement{Name{"foo", "bar"}},
- },
- err: "xml: end tag </bar> without start tag",
- }, {
- desc: "mismatching end tag local name",
- toks: []Token{
- StartElement{Name{"", "foo"}, nil},
- EndElement{Name{"", "bar"}},
- },
- err: "xml: end tag </bar> does not match start tag <foo>",
- want: `<foo>`,
- }, {
- desc: "mismatching end tag namespace",
- toks: []Token{
- StartElement{Name{"space", "foo"}, nil},
- EndElement{Name{"another", "foo"}},
- },
- err: "xml: end tag </foo> in namespace another does not match start tag <foo> in namespace space",
- want: `<space:foo xmlns:space="space">`,
- }, {
- desc: "start element with explicit namespace",
- toks: []Token{
- StartElement{Name{"space", "local"}, []Attr{
- {Name{"xmlns", "x"}, "space"},
- {Name{"space", "foo"}, "value"},
- }},
- },
- want: `<x:local xmlns:x="space" x:foo="value">`,
- }, {
- desc: "start element with explicit namespace and colliding prefix",
- toks: []Token{
- StartElement{Name{"space", "local"}, []Attr{
- {Name{"xmlns", "x"}, "space"},
- {Name{"space", "foo"}, "value"},
- {Name{"x", "bar"}, "other"},
- }},
- },
- want: `<x:local xmlns:x_1="x" xmlns:x="space" x:foo="value" x_1:bar="other">`,
- }, {
- desc: "start element using previously defined namespace",
- toks: []Token{
- StartElement{Name{"", "local"}, []Attr{
- {Name{"xmlns", "x"}, "space"},
- }},
- StartElement{Name{"space", "foo"}, []Attr{
- {Name{"space", "x"}, "y"},
- }},
- },
- want: `<local xmlns:x="space"><x:foo x:x="y">`,
- }, {
- desc: "nested name space with same prefix",
- toks: []Token{
- StartElement{Name{"", "foo"}, []Attr{
- {Name{"xmlns", "x"}, "space1"},
- }},
- StartElement{Name{"", "foo"}, []Attr{
- {Name{"xmlns", "x"}, "space2"},
- }},
- StartElement{Name{"", "foo"}, []Attr{
- {Name{"space1", "a"}, "space1 value"},
- {Name{"space2", "b"}, "space2 value"},
- }},
- EndElement{Name{"", "foo"}},
- EndElement{Name{"", "foo"}},
- StartElement{Name{"", "foo"}, []Attr{
- {Name{"space1", "a"}, "space1 value"},
- {Name{"space2", "b"}, "space2 value"},
- }},
- },
- want: `<foo xmlns:x="space1"><foo xmlns:x="space2"><foo xmlns:space1="space1" space1:a="space1 value" x:b="space2 value"></foo></foo><foo xmlns:space2="space2" x:a="space1 value" space2:b="space2 value">`,
- }, {
- desc: "start element defining several prefixes for the same name space",
- toks: []Token{
- StartElement{Name{"space", "foo"}, []Attr{
- {Name{"xmlns", "a"}, "space"},
- {Name{"xmlns", "b"}, "space"},
- {Name{"space", "x"}, "value"},
- }},
- },
- want: `<a:foo xmlns:a="space" a:x="value">`,
- }, {
- desc: "nested element redefines name space",
- toks: []Token{
- StartElement{Name{"", "foo"}, []Attr{
- {Name{"xmlns", "x"}, "space"},
- }},
- StartElement{Name{"space", "foo"}, []Attr{
- {Name{"xmlns", "y"}, "space"},
- {Name{"space", "a"}, "value"},
- }},
- },
- want: `<foo xmlns:x="space"><x:foo x:a="value">`,
- }, {
- desc: "nested element creates alias for default name space",
- toks: []Token{
- StartElement{Name{"space", "foo"}, []Attr{
- {Name{"", "xmlns"}, "space"},
- }},
- StartElement{Name{"space", "foo"}, []Attr{
- {Name{"xmlns", "y"}, "space"},
- {Name{"space", "a"}, "value"},
- }},
- },
- want: `<foo xmlns="space"><foo xmlns:y="space" y:a="value">`,
- }, {
- desc: "nested element defines default name space with existing prefix",
- toks: []Token{
- StartElement{Name{"", "foo"}, []Attr{
- {Name{"xmlns", "x"}, "space"},
- }},
- StartElement{Name{"space", "foo"}, []Attr{
- {Name{"", "xmlns"}, "space"},
- {Name{"space", "a"}, "value"},
- }},
- },
- want: `<foo xmlns:x="space"><foo xmlns="space" x:a="value">`,
- }, {
- desc: "nested element uses empty attribute name space when default ns defined",
- toks: []Token{
- StartElement{Name{"space", "foo"}, []Attr{
- {Name{"", "xmlns"}, "space"},
- }},
- StartElement{Name{"space", "foo"}, []Attr{
- {Name{"", "attr"}, "value"},
- }},
- },
- want: `<foo xmlns="space"><foo attr="value">`,
- }, {
- desc: "redefine xmlns",
- toks: []Token{
- StartElement{Name{"", "foo"}, []Attr{
- {Name{"foo", "xmlns"}, "space"},
- }},
- },
- err: `xml: cannot redefine xmlns attribute prefix`,
- }, {
- desc: "xmlns with explicit name space #1",
- toks: []Token{
- StartElement{Name{"space", "foo"}, []Attr{
- {Name{"xml", "xmlns"}, "space"},
- }},
- },
- want: `<foo xmlns="space">`,
- }, {
- desc: "xmlns with explicit name space #2",
- toks: []Token{
- StartElement{Name{"space", "foo"}, []Attr{
- {Name{xmlURL, "xmlns"}, "space"},
- }},
- },
- want: `<foo xmlns="space">`,
- }, {
- desc: "empty name space declaration is ignored",
- toks: []Token{
- StartElement{Name{"", "foo"}, []Attr{
- {Name{"xmlns", "foo"}, ""},
- }},
- },
- want: `<foo>`,
- }, {
- desc: "attribute with no name is ignored",
- toks: []Token{
- StartElement{Name{"", "foo"}, []Attr{
- {Name{"", ""}, "value"},
- }},
- },
- want: `<foo>`,
- }, {
- desc: "namespace URL with non-valid name",
- toks: []Token{
- StartElement{Name{"/34", "foo"}, []Attr{
- {Name{"/34", "x"}, "value"},
- }},
- },
- want: `<_:foo xmlns:_="/34" _:x="value">`,
- }, {
- desc: "nested element resets default namespace to empty",
- toks: []Token{
- StartElement{Name{"space", "foo"}, []Attr{
- {Name{"", "xmlns"}, "space"},
- }},
- StartElement{Name{"", "foo"}, []Attr{
- {Name{"", "xmlns"}, ""},
- {Name{"", "x"}, "value"},
- {Name{"space", "x"}, "value"},
- }},
- },
- want: `<foo xmlns="space"><foo xmlns:space="space" xmlns="" x="value" space:x="value">`,
- }, {
- desc: "nested element requires empty default name space",
- toks: []Token{
- StartElement{Name{"space", "foo"}, []Attr{
- {Name{"", "xmlns"}, "space"},
- }},
- StartElement{Name{"", "foo"}, nil},
- },
- want: `<foo xmlns="space"><foo xmlns="">`,
- }, {
- desc: "attribute uses name space from xmlns",
- toks: []Token{
- StartElement{Name{"some/space", "foo"}, []Attr{
- {Name{"", "attr"}, "value"},
- {Name{"some/space", "other"}, "other value"},
- }},
- },
- want: `<space:foo xmlns:space="some/space" attr="value" space:other="other value">`,
- }, {
- desc: "default name space should not be used by attributes",
- toks: []Token{
- StartElement{Name{"space", "foo"}, []Attr{
- {Name{"", "xmlns"}, "space"},
- {Name{"xmlns", "bar"}, "space"},
- {Name{"space", "baz"}, "foo"},
- }},
- StartElement{Name{"space", "baz"}, nil},
- EndElement{Name{"space", "baz"}},
- EndElement{Name{"space", "foo"}},
- },
- want: `<foo xmlns:bar="space" xmlns="space" bar:baz="foo"><baz></baz></foo>`,
- }, {
- desc: "default name space not used by attributes, not explicitly defined",
- toks: []Token{
- StartElement{Name{"space", "foo"}, []Attr{
- {Name{"", "xmlns"}, "space"},
- {Name{"space", "baz"}, "foo"},
- }},
- StartElement{Name{"space", "baz"}, nil},
- EndElement{Name{"space", "baz"}},
- EndElement{Name{"space", "foo"}},
- },
- want: `<foo xmlns:space="space" xmlns="space" space:baz="foo"><baz></baz></foo>`,
- }, {
- desc: "impossible xmlns declaration",
- toks: []Token{
- StartElement{Name{"", "foo"}, []Attr{
- {Name{"", "xmlns"}, "space"},
- }},
- StartElement{Name{"space", "bar"}, []Attr{
- {Name{"space", "attr"}, "value"},
- }},
- },
- want: `<foo><space:bar xmlns:space="space" space:attr="value">`,
- }}
- func TestEncodeToken(t *testing.T) {
- loop:
- for i, tt := range encodeTokenTests {
- var buf bytes.Buffer
- enc := NewEncoder(&buf)
- var err error
- for j, tok := range tt.toks {
- err = enc.EncodeToken(tok)
- if err != nil && j < len(tt.toks)-1 {
- t.Errorf("#%d %s token #%d: %v", i, tt.desc, j, err)
- continue loop
- }
- }
- errorf := func(f string, a ...interface{}) {
- t.Errorf("#%d %s token #%d:%s", i, tt.desc, len(tt.toks)-1, fmt.Sprintf(f, a...))
- }
- switch {
- case tt.err != "" && err == nil:
- errorf(" expected error; got none")
- continue
- case tt.err == "" && err != nil:
- errorf(" got error: %v", err)
- continue
- case tt.err != "" && err != nil && tt.err != err.Error():
- errorf(" error mismatch; got %v, want %v", err, tt.err)
- continue
- }
- if err := enc.Flush(); err != nil {
- errorf(" %v", err)
- continue
- }
- if got := buf.String(); got != tt.want {
- errorf("\ngot %v\nwant %v", got, tt.want)
- continue
- }
- }
- }
- func TestProcInstEncodeToken(t *testing.T) {
- var buf bytes.Buffer
- enc := NewEncoder(&buf)
- if err := enc.EncodeToken(ProcInst{"xml", []byte("Instruction")}); err != nil {
- t.Fatalf("enc.EncodeToken: expected to be able to encode xml target ProcInst as first token, %s", err)
- }
- if err := enc.EncodeToken(ProcInst{"Target", []byte("Instruction")}); err != nil {
- t.Fatalf("enc.EncodeToken: expected to be able to add non-xml target ProcInst")
- }
- if err := enc.EncodeToken(ProcInst{"xml", []byte("Instruction")}); err == nil {
- t.Fatalf("enc.EncodeToken: expected to not be allowed to encode xml target ProcInst when not first token")
- }
- }
- func TestDecodeEncode(t *testing.T) {
- var in, out bytes.Buffer
- in.WriteString(`<?xml version="1.0" encoding="UTF-8"?>
- <?Target Instruction?>
- <root>
- </root>
- `)
- dec := NewDecoder(&in)
- enc := NewEncoder(&out)
- for tok, err := dec.Token(); err == nil; tok, err = dec.Token() {
- err = enc.EncodeToken(tok)
- if err != nil {
- t.Fatalf("enc.EncodeToken: Unable to encode token (%#v), %v", tok, err)
- }
- }
- }
- // Issue 9796. Used to fail with GORACE="halt_on_error=1" -race.
- func TestRace9796(t *testing.T) {
- type A struct{}
- type B struct {
- C []A `xml:"X>Y"`
- }
- var wg sync.WaitGroup
- for i := 0; i < 2; i++ {
- wg.Add(1)
- go func() {
- Marshal(B{[]A{{}}})
- wg.Done()
- }()
- }
- wg.Wait()
- }
- func TestIsValidDirective(t *testing.T) {
- testOK := []string{
- "<>",
- "< < > >",
- "<!DOCTYPE '<' '>' '>' <!--nothing-->>",
- "<!DOCTYPE doc [ <!ELEMENT doc ANY> <!ELEMENT doc ANY> ]>",
- "<!DOCTYPE doc [ <!ELEMENT doc \"ANY> '<' <!E\" LEMENT '>' doc ANY> ]>",
- "<!DOCTYPE doc <!-- just>>>> a < comment --> [ <!ITEM anything> ] >",
- }
- testKO := []string{
- "<",
- ">",
- "<!--",
- "-->",
- "< > > < < >",
- "<!dummy <!-- > -->",
- "<!DOCTYPE doc '>",
- "<!DOCTYPE doc '>'",
- "<!DOCTYPE doc <!--comment>",
- }
- for _, s := range testOK {
- if !isValidDirective(Directive(s)) {
- t.Errorf("Directive %q is expected to be valid", s)
- }
- }
- for _, s := range testKO {
- if isValidDirective(Directive(s)) {
- t.Errorf("Directive %q is expected to be invalid", s)
- }
- }
- }
- // Issue 11719. EncodeToken used to silently eat tokens with an invalid type.
- func TestSimpleUseOfEncodeToken(t *testing.T) {
- var buf bytes.Buffer
- enc := NewEncoder(&buf)
- if err := enc.EncodeToken(&StartElement{Name: Name{"", "object1"}}); err == nil {
- t.Errorf("enc.EncodeToken: pointer type should be rejected")
- }
- if err := enc.EncodeToken(&EndElement{Name: Name{"", "object1"}}); err == nil {
- t.Errorf("enc.EncodeToken: pointer type should be rejected")
- }
- if err := enc.EncodeToken(StartElement{Name: Name{"", "object2"}}); err != nil {
- t.Errorf("enc.EncodeToken: StartElement %s", err)
- }
- if err := enc.EncodeToken(EndElement{Name: Name{"", "object2"}}); err != nil {
- t.Errorf("enc.EncodeToken: EndElement %s", err)
- }
- if err := enc.EncodeToken(Universe{}); err == nil {
- t.Errorf("enc.EncodeToken: invalid type not caught")
- }
- if err := enc.Flush(); err != nil {
- t.Errorf("enc.Flush: %s", err)
- }
- if buf.Len() == 0 {
- t.Errorf("enc.EncodeToken: empty buffer")
- }
- want := "<object2></object2>"
- if buf.String() != want {
- t.Errorf("enc.EncodeToken: expected %q; got %q", want, buf.String())
- }
- }
|