Skip to content

Commit 9593b74

Browse files
SamWhitedianlancetaylor
authored andcommitted
encoding/xml: add decode wrapper
Fixes #19480 Change-Id: I5a621507279d5bb1f3991b7a412d9a63039d464b Reviewed-on: https://go-review.googlesource.com/38791 Run-TryBot: Sam Whited <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]>
1 parent a696277 commit 9593b74

File tree

2 files changed

+101
-0
lines changed

2 files changed

+101
-0
lines changed

src/encoding/xml/xml.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,23 @@ func CopyToken(t Token) Token {
135135
return t
136136
}
137137

138+
// A TokenReader is anything that can decode a stream of XML tokens, including a
139+
// Decoder.
140+
//
141+
// When Token encounters an error or end-of-file condition after successfully
142+
// reading a token, it returns the token. It may return the (non-nil) error from
143+
// the same call or return the error (and a nil token) from a subsequent call.
144+
// An instance of this general case is that a TokenReader returning a non-nil
145+
// token at the end of the token stream may return either io.EOF or a nil error.
146+
// The next Read should return nil, io.EOF.
147+
//
148+
// Implementations of Token are discouraged from returning a nil token with a
149+
// nil error. Callers should treat a return of nil, nil as indicating that
150+
// nothing happened; in particular it does not indicate EOF.
151+
type TokenReader interface {
152+
Token() (Token, error)
153+
}
154+
138155
// A Decoder represents an XML parser reading a particular input stream.
139156
// The parser assumes that its input is encoded in UTF-8.
140157
type Decoder struct {
@@ -190,6 +207,7 @@ type Decoder struct {
190207
DefaultSpace string
191208

192209
r io.ByteReader
210+
t TokenReader
193211
buf bytes.Buffer
194212
saved *bytes.Buffer
195213
stk *stack
@@ -219,6 +237,22 @@ func NewDecoder(r io.Reader) *Decoder {
219237
return d
220238
}
221239

240+
// NewTokenDecoder creates a new XML parser using an underlying token stream.
241+
func NewTokenDecoder(t TokenReader) *Decoder {
242+
// Is it already a Decoder?
243+
if d, ok := t.(*Decoder); ok {
244+
return d
245+
}
246+
d := &Decoder{
247+
ns: make(map[string]string),
248+
t: t,
249+
nextByte: -1,
250+
line: 1,
251+
Strict: true,
252+
}
253+
return d
254+
}
255+
222256
// Token returns the next XML token in the input stream.
223257
// At the end of the input stream, Token returns nil, io.EOF.
224258
//
@@ -243,6 +277,9 @@ func NewDecoder(r io.Reader) *Decoder {
243277
// If Token encounters an unrecognized name space prefix,
244278
// it uses the prefix as the Space rather than report an error.
245279
func (d *Decoder) Token() (Token, error) {
280+
if d.t != nil {
281+
return d.t.Token()
282+
}
246283
var t Token
247284
var err error
248285
if d.stk != nil && d.stk.kind == stkEOF {

src/encoding/xml/xml_test.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -797,3 +797,67 @@ func TestIssue12417(t *testing.T) {
797797
}
798798
}
799799
}
800+
801+
func tokenMap(mapping func(t Token) Token) func(TokenReader) TokenReader {
802+
return func(src TokenReader) TokenReader {
803+
return mapper{
804+
t: src,
805+
f: mapping,
806+
}
807+
}
808+
}
809+
810+
type mapper struct {
811+
t TokenReader
812+
f func(Token) Token
813+
}
814+
815+
func (m mapper) Token() (Token, error) {
816+
tok, err := m.t.Token()
817+
if err != nil {
818+
return nil, err
819+
}
820+
return m.f(tok), nil
821+
}
822+
823+
func TestNewTokenDecoderIdempotent(t *testing.T) {
824+
d := NewDecoder(strings.NewReader(`<br/>`))
825+
d2 := NewTokenDecoder(d)
826+
if d != d2 {
827+
t.Error("NewTokenDecoder did not detect underlying Decoder")
828+
}
829+
}
830+
831+
func TestWrapDecoder(t *testing.T) {
832+
d := NewDecoder(strings.NewReader(`<quote>[Re-enter Clown with a letter, and FABIAN]</quote>`))
833+
m := tokenMap(func(t Token) Token {
834+
switch tok := t.(type) {
835+
case StartElement:
836+
if tok.Name.Local == "quote" {
837+
tok.Name.Local = "blocking"
838+
return tok
839+
}
840+
case EndElement:
841+
if tok.Name.Local == "quote" {
842+
tok.Name.Local = "blocking"
843+
return tok
844+
}
845+
}
846+
return t
847+
})
848+
849+
d = NewTokenDecoder(m(d))
850+
851+
o := struct {
852+
XMLName Name `xml:"blocking"`
853+
Chardata string `xml:",chardata"`
854+
}{}
855+
856+
if err := d.Decode(&o); err != nil {
857+
t.Fatal("Got unexpected error while decoding:", err)
858+
}
859+
860+
if o.Chardata != "[Re-enter Clown with a letter, and FABIAN]" {
861+
t.Fatalf("Got unexpected chardata: `%s`\n", o.Chardata)
862+
}
863+
}

0 commit comments

Comments
 (0)