]> Cypherpunks repositories - gostls13.git/commitdiff
make reader more useful
authorRuss Cox <rsc@golang.org>
Wed, 7 Oct 2009 05:03:47 +0000 (22:03 -0700)
committerRuss Cox <rsc@golang.org>
Wed, 7 Oct 2009 05:03:47 +0000 (22:03 -0700)
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

src/pkg/xml/read.go

index 025890b8fdfd73a348bfbd53bf53d55973d089bf..e2d07b913b1b473f043b0e9b8b8073ec936a4b1a 100644 (file)
@@ -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: