mirror of
https://github.com/golang/go
synced 2024-09-25 11:10:13 -06: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:
parent
c2ec9583a0
commit
cfdb3a5639
@ -94,6 +94,8 @@ import (
|
||||
// * If the XML element contains a sub-element whose name
|
||||
// matches a struct field whose tag is neither "attr" nor "chardata",
|
||||
// Unmarshal maps the sub-element to that struct field.
|
||||
// Otherwise, if the struct has a field named Any, unmarshal
|
||||
// maps the sub-element to that struct field.
|
||||
//
|
||||
// Unmarshal maps an XML element to a string or []byte by saving the
|
||||
// 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
|
||||
// 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 {
|
||||
v, ok := reflect.NewValue(val).(*reflect.PtrValue);
|
||||
if !ok {
|
||||
@ -108,14 +118,9 @@ func Unmarshal(r io.Reader, val interface{}) os.Error {
|
||||
}
|
||||
p := NewParser(r);
|
||||
elem := v.Elem();
|
||||
for {
|
||||
err := p.unmarshal(elem, nil);
|
||||
if err != nil {
|
||||
if err == os.EOF {
|
||||
break;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
err := p.unmarshal(elem, nil);
|
||||
if err != nil {
|
||||
return err;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
@ -126,6 +131,20 @@ func (e UnmarshalError) String() string {
|
||||
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.
|
||||
func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error {
|
||||
// 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 (
|
||||
data []byte;
|
||||
saveData reflect.Value;
|
||||
@ -149,6 +174,9 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error {
|
||||
styp *reflect.StructType;
|
||||
)
|
||||
switch v := val.(type) {
|
||||
case *reflect.BoolValue:
|
||||
v.Set(true);
|
||||
|
||||
case *reflect.SliceValue:
|
||||
typ := v.Type().(*reflect.SliceType);
|
||||
if _, ok := typ.Elem().(*reflect.Uint8Type); ok {
|
||||
@ -182,6 +210,11 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error {
|
||||
saveData = v;
|
||||
|
||||
case *reflect.StructValue:
|
||||
if _, ok := v.Interface().(Name); ok {
|
||||
v.Set(reflect.NewValue(start.Name).(*reflect.StructValue));
|
||||
break;
|
||||
}
|
||||
|
||||
sv = v;
|
||||
typ := sv.Type().(*reflect.StructType);
|
||||
styp = typ;
|
||||
@ -257,8 +290,11 @@ Loop:
|
||||
switch t := tok.(type) {
|
||||
case StartElement:
|
||||
// Sub-element.
|
||||
// Look up by tag name.
|
||||
// If that fails, fall back to mop-up field named "Any".
|
||||
if sv != nil {
|
||||
k := strings.ToLower(t.Name.Local);
|
||||
any := -1;
|
||||
for i, n := 0, styp.NumField(); i < n; i++ {
|
||||
f := styp.Field(i);
|
||||
if strings.ToLower(f.Name) == k {
|
||||
@ -267,10 +303,19 @@ 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.
|
||||
if err := p.skip(); err != nil {
|
||||
if err := p.Skip(); err != nil {
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -301,7 +346,7 @@ Loop:
|
||||
// Read tokens until we find the end element.
|
||||
// Token is taking care of making sure the
|
||||
// end element matches the start element we saw.
|
||||
func (p *Parser) skip() os.Error {
|
||||
func (p *Parser) Skip() os.Error {
|
||||
for {
|
||||
tok, err := p.Token();
|
||||
if err != nil {
|
||||
@ -309,7 +354,7 @@ func (p *Parser) skip() os.Error {
|
||||
}
|
||||
switch t := tok.(type) {
|
||||
case StartElement:
|
||||
if err := p.skip(); err != nil {
|
||||
if err := p.Skip(); err != nil {
|
||||
return err;
|
||||
}
|
||||
case EndElement:
|
||||
|
Loading…
Reference in New Issue
Block a user