summaryrefslogtreecommitdiff
path: root/src/pkg/xml/read.go
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2009-10-06 22:03:47 -0700
committerRuss Cox <rsc@golang.org>2009-10-06 22:03:47 -0700
commit8a7b8449de6b93152be1849efcbd3adc953f512d (patch)
treea684b5b7e835750e857348c7cb337aad97cd0bff /src/pkg/xml/read.go
parent6c2652e6fd54ce34ca3be95540c54cebb52bfece (diff)
downloadgolang-8a7b8449de6b93152be1849efcbd3adc953f512d.tar.gz
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
Diffstat (limited to 'src/pkg/xml/read.go')
-rw-r--r--src/pkg/xml/read.go67
1 files changed, 56 insertions, 11 deletions
diff --git a/src/pkg/xml/read.go b/src/pkg/xml/read.go
index 025890b8f..e2d07b913 100644
--- a/src/pkg/xml/read.go
+++ b/src/pkg/xml/read.go
@@ -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: