1
0
mirror of https://github.com/golang/go synced 2024-11-12 09:50:21 -07:00

make reader more useful

for lower-level clients:
	* expose p.Skip
	* expose p.Unmarshal
	* wildcard struct field "Any"
	* unmarshal into bool
	* unmarshal into xml.Name
	* unmarshal into pointer

R=r
DELTA=61  (50 added, 5 deleted, 6 changed)
OCL=35372
CL=35422
This commit is contained in:
Russ Cox 2009-10-06 22:03:47 -07:00
parent c2ec9583a0
commit cfdb3a5639

View File

@ -94,6 +94,8 @@ import (
// * 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 struct 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
// maps the sub-element to that struct field.
// //
// Unmarshal maps an XML element to a string or []byte by saving the // Unmarshal maps an XML element to a string or []byte by saving the
// concatenation of that elements character data in the string or []byte. // concatenation of that elements character data in the string or []byte.
@ -101,6 +103,14 @@ import (
// Unmarshal maps an XML element to a slice by extending the length // Unmarshal maps an XML element to a slice by extending the length
// of the slice and mapping the element to the newly created value. // of the slice and mapping the element to the newly created value.
// //
// Unmarshal maps an XML element to a bool by setting the bool to true.
//
// Unmarshal maps an XML element to an xml.Name by recording the
// element name.
//
// Unmarshal maps an XML element to a pointer by setting the pointer
// to a freshly allocated value and then mapping the element to that value.
//
func Unmarshal(r io.Reader, val interface{}) os.Error { func Unmarshal(r io.Reader, val interface{}) os.Error {
v, ok := reflect.NewValue(val).(*reflect.PtrValue); v, ok := reflect.NewValue(val).(*reflect.PtrValue);
if !ok { if !ok {
@ -108,14 +118,9 @@ func Unmarshal(r io.Reader, val interface{}) os.Error {
} }
p := NewParser(r); p := NewParser(r);
elem := v.Elem(); elem := v.Elem();
for { err := p.unmarshal(elem, nil);
err := p.unmarshal(elem, nil); if err != nil {
if err != nil { return err;
if err == os.EOF {
break;
}
return err;
}
} }
return nil; return nil;
} }
@ -126,6 +131,20 @@ func (e UnmarshalError) String() string {
return string(e); return string(e);
} }
// The Parser's Unmarshal method is like xml.Unmarshal
// except that it can be passed a pointer to the initial start element,
// useful when a client reads some raw XML tokens itself
// but also defers to Unmarshal for some elements.
// Passing a nil start element indicates that Unmarshal should
// read the token stream to find the start element.
func (p *Parser) Unmarshal(val interface{}, start *StartElement) os.Error {
v, ok := reflect.NewValue(val).(*reflect.PtrValue);
if !ok {
return os.NewError("non-pointer passed to Unmarshal");
}
return p.unmarshal(v.Elem(), start);
}
// Unmarshal a single XML element into val. // Unmarshal a single XML element into val.
func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error { func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error {
// Find start element if we need it. // Find start element if we need it.
@ -142,6 +161,12 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error {
} }
} }
if pv, ok := val.(*reflect.PtrValue); ok {
zv := reflect.MakeZero(pv.Type().(*reflect.PtrType).Elem());
pv.PointTo(zv);
val = zv;
}
var ( var (
data []byte; data []byte;
saveData reflect.Value; saveData reflect.Value;
@ -149,6 +174,9 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error {
styp *reflect.StructType; styp *reflect.StructType;
) )
switch v := val.(type) { switch v := val.(type) {
case *reflect.BoolValue:
v.Set(true);
case *reflect.SliceValue: case *reflect.SliceValue:
typ := v.Type().(*reflect.SliceType); typ := v.Type().(*reflect.SliceType);
if _, ok := typ.Elem().(*reflect.Uint8Type); ok { if _, ok := typ.Elem().(*reflect.Uint8Type); ok {
@ -182,6 +210,11 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error {
saveData = v; saveData = v;
case *reflect.StructValue: case *reflect.StructValue:
if _, ok := v.Interface().(Name); ok {
v.Set(reflect.NewValue(start.Name).(*reflect.StructValue));
break;
}
sv = v; sv = v;
typ := sv.Type().(*reflect.StructType); typ := sv.Type().(*reflect.StructType);
styp = typ; styp = typ;
@ -257,8 +290,11 @@ Loop:
switch t := tok.(type) { switch t := tok.(type) {
case StartElement: case StartElement:
// Sub-element. // Sub-element.
// Look up by tag name.
// If that fails, fall back to mop-up field named "Any".
if sv != nil { if sv != nil {
k := strings.ToLower(t.Name.Local); k := strings.ToLower(t.Name.Local);
any := -1;
for i, n := 0, styp.NumField(); i < n; i++ { for i, n := 0, styp.NumField(); i < n; i++ {
f := styp.Field(i); f := styp.Field(i);
if strings.ToLower(f.Name) == k { if strings.ToLower(f.Name) == k {
@ -267,10 +303,19 @@ Loop:
} }
continue Loop; continue Loop;
} }
if any < 0 && f.Name == "Any" {
any = i;
}
}
if any >= 0 {
if err := p.unmarshal(sv.FieldByIndex(styp.Field(any).Index), &t); err != nil {
return err;
}
continue Loop;
} }
} }
// Not saving sub-element but still have to skip over it. // Not saving sub-element but still have to skip over it.
if err := p.skip(); err != nil { if err := p.Skip(); err != nil {
return err; return err;
} }
@ -301,7 +346,7 @@ Loop:
// Read tokens until we find the end element. // Read tokens until we find the end element.
// Token is taking care of making sure the // Token is taking care of making sure the
// end element matches the start element we saw. // end element matches the start element we saw.
func (p *Parser) skip() os.Error { func (p *Parser) Skip() os.Error {
for { for {
tok, err := p.Token(); tok, err := p.Token();
if err != nil { if err != nil {
@ -309,7 +354,7 @@ func (p *Parser) skip() os.Error {
} }
switch t := tok.(type) { switch t := tok.(type) {
case StartElement: case StartElement:
if err := p.skip(); err != nil { if err := p.Skip(); err != nil {
return err; return err;
} }
case EndElement: case EndElement: