//
// Unmarshal maps an XML element to a struct using the following rules:
//
+// * If the struct has a field of type []byte or string with tag "innerxml",
+// Unmarshal accumulates the raw XML nested inside the element
+// in that field. The rest of the rules still apply.
+//
// * If the struct has a field named XMLName of type xml.Name,
// Unmarshal records the element name in that field.
//
}
var (
- data []byte
- saveData reflect.Value
- comment []byte
- saveComment reflect.Value
- sv *reflect.StructValue
- styp *reflect.StructType
+ data []byte
+ saveData reflect.Value
+ comment []byte
+ saveComment reflect.Value
+ saveXML reflect.Value
+ saveXMLIndex int
+ saveXMLData []byte
+ sv *reflect.StructValue
+ styp *reflect.StructType
)
switch v := val.(type) {
default:
if saveData == nil {
saveData = sv.FieldByIndex(f.Index)
}
+
+ case "innerxml":
+ if saveXML == nil {
+ saveXML = sv.FieldByIndex(f.Index)
+ if p.saved == nil {
+ saveXMLIndex = 0
+ p.saved = new(bytes.Buffer)
+ } else {
+ saveXMLIndex = p.savedOffset()
+ }
+ }
}
}
}
// Process sub-elements along the way.
Loop:
for {
+ var savedOffset int
+ if saveXML != nil {
+ savedOffset = p.savedOffset()
+ }
tok, err := p.Token()
if err != nil {
return err
}
case EndElement:
+ if saveXML != nil {
+ saveXMLData = p.saved.Bytes()[saveXMLIndex:savedOffset]
+ if saveXMLIndex == 0 {
+ p.saved = nil
+ }
+ }
break Loop
case CharData:
t.Set(reflect.NewValue(comment).(*reflect.SliceValue))
}
+ switch t := saveXML.(type) {
+ case *reflect.StringValue:
+ t.Set(string(saveXMLData))
+ case *reflect.SliceValue:
+ t.Set(reflect.NewValue(saveXMLData).(*reflect.SliceValue))
+ }
+
return nil
}
t.Fatalf("Unmarshal: %s", err)
}
if !reflect.DeepEqual(f, rssFeed) {
- t.Fatalf("have %#v\nwant %#v\n\n%#v", f)
+ t.Fatalf("have %#v\nwant %#v", f, rssFeed)
}
}
}
type Person struct {
- Name string
- URI string
- Email string
+ Name string
+ URI string
+ Email string
+ InnerXML string "innerxml"
}
type Text struct {
Id: "http://codereview.appspot.com/",
Updated: "2009-10-04T01:35:58+00:00",
Author: Person{
- Name: "rietveld",
+ Name: "rietveld",
+ InnerXML: "<name>rietveld</name>",
},
Entry: []Entry{
Entry{
},
Updated: "2009-10-04T01:35:58+00:00",
Author: Person{
- Name: "email-address-removed",
+ Name: "email-address-removed",
+ InnerXML: "<name>email-address-removed</name>",
},
Id: "urn:md5:134d9179c41f806be79b3a5f7877d19a",
Summary: Text{
},
Updated: "2009-10-03T23:02:17+00:00",
Author: Person{
- Name: "email-address-removed",
+ Name: "email-address-removed",
+ InnerXML: "<name>email-address-removed</name>",
},
Id: "urn:md5:0a2a4f19bb815101f0ba2904aed7c35a",
Summary: Text{
r io.ReadByter
buf bytes.Buffer
+ saved *bytes.Buffer
stk *stack
free *stack
needClose bool
if p.err != nil {
return 0, false
}
+ if p.saved != nil {
+ p.saved.WriteByte(b)
+ }
}
if b == '\n' {
p.line++
return b, true
}
+// Return saved offset.
+// If we did ungetc (nextByte >= 0), have to back up one.
+func (p *Parser) savedOffset() int {
+ n := p.saved.Len()
+ if p.nextByte >= 0 {
+ n--
+ }
+ return n
+}
+
// Must read a single byte.
// If there is no byte to read,
// set p.err to SyntaxError("unexpected EOF")