// makePath finds or creates a tree of Scanners. // It returns the leaf node Scanner for the path or an error. func (s *Scanner) makePath(path string) (*Scanner, error) { names := strings.SplitN(path, ">", 2) fields := strings.Fields(names[0]) var name xml.Name switch len(fields) { case 0: if len(names) > 1 { return nil, ErrInvalidPath } return s, nil case 1: name.Local = fields[0] case 2: name.Space = fields[0] name.Local = fields[1] default: return nil, ErrInvalidPath } s2, ok := s.tree[name] if !ok { s2 = NewScanner() s.tree[name] = s2 } if len(names) == 1 { return s2.makePath("") } return s2.makePath(names[1]) }
// SOAP arrays are declared as follows (unimportant fields ellided): // // <xs:complexType name="Array"> // <xs:attribute name="arrayType" type="xs:string" /> // <xs:any namespace="##any" minOccurs="0" maxOccurs="unbounded" /> // </xs:complexType> // // Then schemas that want to declare a fixed-type soap array do so like this: // // <xs:complexType name="IntArray"> // <xs:complexContent> // <xs:restriction base="soapenc:Array> // <xs:attribute ref="soapenc:arrayType" wsdl:arrayType="xs:int[]" /> // </xs:restriction> // </xs:complexContent> // </xs:complexType> // // XML Schema is wonderful, aint it? func (cfg *Config) parseSOAPArrayType(s xsd.Schema, t xsd.Type) xsd.Type { const soapenc = "http://schemas.xmlsoap.org/soap/encoding/" const wsdl = "http://schemas.xmlsoap.org/wsdl/" var itemType xml.Name c, ok := t.(*xsd.ComplexType) if !ok { return t } var attr []xsd.Attribute for _, v := range c.Attributes { if v.Name.Local != "arrayType" { attr = append(attr, v) continue } for _, a := range v.Attr { if (a.Name != xml.Name{wsdl, "arrayType"}) { continue } itemType = v.Resolve(a.Value) break } break } if itemType.Local == "" { return c } itemType.Local = strings.TrimSpace(itemType.Local) itemType.Local = strings.TrimSuffix(itemType.Local, "[]") if b := s.FindType(itemType); b != nil { c = cfg.overrideWildcardType(c, b) } else { cfg.logf("could not lookup item type %q in namespace %q", itemType.Local, itemType.Space) } // Have to remove arrayType from the "base" type, without affecting // others inheriting from this type. basep, ok := c.Base.(*xsd.ComplexType) if !ok { cfg.logf("type %s derives from non-complexType %s", c.Name.Local, xsd.XMLName(c.Base).Local) return c } base := *basep base.Attributes = make([]xsd.Attribute, 0, len(basep.Attributes)-1) for _, v := range basep.Attributes { if v.Name.Local != "arrayType" { base.Attributes = append(base.Attributes, v) } } c.Base = &base c.Attributes = attr return c }
// Unmarshal parses the XML encoding of the Element and stores the result // in the value pointed to by v. Unmarshal follows the same rules as // xml.Unmarshal, but only parses the portion of the XML document // contained by the Element. func (el *Element) Unmarshal(v interface{}) error { start := el.StartElement for _, ns := range el.ns { name := xml.Name{"", "xmlns"} if ns.Local != "" { name.Local += ":" + ns.Local } start.Attr = append(start.Attr, xml.Attr{name, ns.Space}) } if start.Name.Space != "" { for i := len(el.ns) - 1; i >= 0; i-- { if el.ns[i].Space == start.Name.Space { start.Name.Space = "" start.Name.Local = el.ns[i].Local + ":" + start.Name.Local break } } if start.Name.Space != "" { return fmt.Errorf("Could not find namespace prefix for %q when decoding %s", start.Name.Space, start.Name.Local) } } var buf bytes.Buffer e := xml.NewEncoder(&buf) if err := e.EncodeToken(start); err != nil { return err } if err := e.Flush(); err != nil { return err } // BUG(droyo) The Unmarshal method unmarshals an XML fragment as it // was returned by the Parse method; further modifications to a tree of // Elements are ignored by the Unmarshal method. buf.Write(el.Content) if err := e.EncodeToken(xml.EndElement{start.Name}); err != nil { return err } if err := e.Flush(); err != nil { return err } return xml.Unmarshal(buf.Bytes(), v) }
func xfFunction(f *xpFilt, n *parser.Node) error { spl := strings.Split(n.Val.Val, ":") var name xml.Name if len(spl) == 1 { name.Local = spl[0] } else { name.Space = f.ns[spl[0]] name.Local = spl[1] } fn, ok := intfns.BuiltIn[name] if !ok { fn, ok = f.fns[name] } if ok { args := []tree.Result{} param := n.Left for param != nil { pf := xpFilt{ t: f.t, ctx: f.ctx, ns: f.ns, ctxPos: f.ctxPos, ctxSize: f.ctxSize, fns: f.fns, variables: f.variables, } res, err := exec(&pf, param.Left) if err != nil { return err } args = append(args, res) param = param.Right } filt, err := fn.Call(tree.Ctx{NodeSet: f.ctx.(tree.NodeSet), Size: f.ctxSize, Pos: f.ctxPos + 1}, args...) f.ctx = filt return err } return fmt.Errorf("Unknown function: %s", n.Val.Val) }
// SOAP arrays (and other similar types) are complex types with a single // plural element. We add a post-processing step to flatten it out and provide // marshal/unmarshal methods. func (cfg *Config) soapArrayToSlice(s spec) spec { str, ok := s.expr.(*ast.StructType) if !ok { return s } if len(str.Fields.List) != 1 { return s } slice, ok := str.Fields.List[0].Type.(*ast.ArrayType) if !ok { return s } cfg.debugf("flattening single-element slice struct type %s to []%v", s.name, slice.Elt) tag := gen.TagKey(str.Fields.List[0], "xml") xmltag := xml.Name{"", ",any"} if tag != "" { parts := strings.Split(tag, ",") if len(parts) > 0 { fields := strings.Fields(parts[0]) if len(fields) > 0 { xmltag.Local = fields[len(fields)-1] } if len(fields) > 1 { xmltag.Space = fields[0] } } } itemType := gen.ExprString(slice.Elt) unmarshal, err := gen.Func("UnmarshalXML"). Receiver("a *"+s.name). Args("d *xml.Decoder", "start xml.StartElement"). Returns("err error"). Body(` var tok xml.Token var itemTag = xml.Name{%q, %q} for tok, err = d.Token(); err == nil; tok, err = d.Token() { if tok, ok := tok.(xml.StartElement); ok { var item %s if itemTag.Local != ",any" && itemTag != tok.Name { err = d.Skip() continue } if err = d.DecodeElement(&item, &tok); err == nil { *a = append(*a, item) } } if _, ok := tok.(xml.EndElement); ok { break } } return err `, xmltag.Space, xmltag.Local, itemType).Decl() if err != nil { cfg.logf("error generating UnmarshalXML method of %s: %v", s.name, err) return s } marshal, err := gen.Func("MarshalXML"). Receiver("a *"+s.name). Args("e *xml.Encoder", "start xml.StartElement"). Returns("error"). Body(` tag := xml.StartElement{Name: xml.Name{"", "item"}} for _, elt := range *a { if err := e.EncodeElement(elt, tag); err != nil { return err } } return nil `).Decl() if err != nil { cfg.logf("error generating MarshalXML method of %s: %v", s.name, err) return s } s.expr = slice s.methods = append(s.methods, marshal) s.methods = append(s.methods, unmarshal) if helper := cfg.helper("_unmarshalArray"); helper != nil { s.methods = append(s.methods, helper) } return s }