diff --git a/xsd/parse.go b/xsd/parse.go index 0e1ac4f..c087aaf 100644 --- a/xsd/parse.go +++ b/xsd/parse.go @@ -146,7 +146,10 @@ func Parse(docs ...[]byte) ([]Schema, error) { for _, root := range schema { tns := root.Attr("", "targetNamespace") - s := Schema{TargetNS: tns, Types: make(map[xml.Name]Type)} + efd := parseForm(root.Attr("", "elementFormDefault"), FormOptionUndefined) + afd := parseForm(root.Attr("", "attributeFormDefault"), FormOptionUndefined) + + s := Schema{TargetNS: tns, ElementFormDefault: efd, AttributeFormDefault: afd, Types: make(map[xml.Name]Type)} if err := s.parse(root); err != nil { return nil, err } @@ -404,7 +407,7 @@ func deref(ref, real *xmltree.Element) *xmltree.Element { el := new(xmltree.Element) el.Scope = ref.Scope el.Name = real.Name - el.StartElement.Attr = append([]xml.Attr{}, real.StartElement.Attr...) + el.StartElement.Attr = []xml.Attr{} el.Content = append([]byte{}, real.Content...) el.Children = append([]xmltree.Element{}, real.Children...) @@ -413,11 +416,17 @@ func deref(ref, real *xmltree.Element) *xmltree.Element { hasQName := map[xml.Name]bool{ xml.Name{"", "type"}: true, } - for i, attr := range el.StartElement.Attr { + // Some attribute should not be copied + dontCopy := map[xml.Name]bool{ + xml.Name{"", "name"}: true, + xml.Name{"", "form"}: true, + } + for _, attr := range real.StartElement.Attr { if hasQName[attr.Name] { xmlname := real.Resolve(attr.Value) - attr.Value = ref.Prefix(xmlname) - el.StartElement.Attr[i] = attr + el.SetAttr(attr.Name.Space, attr.Name.Local, ref.Prefix(xmlname)) + } else if !dontCopy[attr.Name] { + el.SetAttr(attr.Name.Space, attr.Name.Local, attr.Value) } } // If there are child elements, rather than checking all children @@ -429,11 +438,16 @@ func deref(ref, real *xmltree.Element) *xmltree.Element { // Attributes added to the reference overwrite attributes in the // referenced element. for _, attr := range ref.StartElement.Attr { - if (attr.Name != xml.Name{"", "ref"}) { + if (attr.Name == xml.Name{"", "ref"}) { + el.SetAttr("", "name", attr.Value) + } else { el.SetAttr(attr.Name.Space, attr.Name.Local, attr.Value) } } + // Referenced elements are always qualified + el.SetAttr("", "form", "qualified") + return el } @@ -582,9 +596,10 @@ func attributeDefaultType(root *xmltree.Element) { var ( isAttr = isElem(schemaNS, "attribute") hasNoType = hasAttrValue("", "type", "") + hasNoRef = hasAttrValue("", "ref", "") anyType = xml.Name{Space: schemaNS, Local: "anySimpleType"} ) - for _, el := range root.SearchFunc(and(isAttr, hasNoType)) { + for _, el := range root.SearchFunc(and(isAttr, hasNoType, hasNoRef)) { el.SetAttr("", "type", el.Prefix(anyType)) } } @@ -598,9 +613,10 @@ func elementDefaultType(root *xmltree.Element) { var ( isElement = isElem(schemaNS, "element") hasNoType = hasAttrValue("", "type", "") + hasNoRef = hasAttrValue("", "ref", "") anyType = xml.Name{Space: schemaNS, Local: "anyType"} ) - for _, el := range root.SearchFunc(and(isElement, hasNoType)) { + for _, el := range root.SearchFunc(and(isElement, hasNoType, hasNoRef)) { el.SetAttr("", "type", el.Prefix(anyType)) } } @@ -658,9 +674,9 @@ func (s *Schema) parseComplexType(root *xmltree.Element) *ComplexType { case "annotation": doc = doc.append(parseAnnotation(el)) case "simpleContent": - t.parseSimpleContent(s.TargetNS, el) + t.parseSimpleContent(s.TargetNS, s.AttributeFormDefault, el) case "complexContent": - t.parseComplexContent(s.TargetNS, el) + t.parseComplexContent(s.TargetNS, s.ElementFormDefault, s.AttributeFormDefault, el) default: stop("unexpected element " + el.Name.Local) } @@ -671,7 +687,7 @@ func (s *Schema) parseComplexType(root *xmltree.Element) *ComplexType { // simpleContent indicates that the content model of the new type // contains only character data and no elements -func (t *ComplexType) parseSimpleContent(ns string, root *xmltree.Element) { +func (t *ComplexType) parseSimpleContent(ns string, afd FormOption, root *xmltree.Element) { var doc annotation t.Mixed = true @@ -685,7 +701,7 @@ func (t *ComplexType) parseSimpleContent(ns string, root *xmltree.Element) { t.Base = parseType(el.Resolve(el.Attr("", "base"))) t.Extends = true for _, v := range el.Search(schemaNS, "attribute") { - t.Attributes = append(t.Attributes, parseAttribute(ns, v)) + t.Attributes = append(t.Attributes, parseAttribute(ns, afd, v)) } } }) @@ -694,7 +710,7 @@ func (t *ComplexType) parseSimpleContent(ns string, root *xmltree.Element) { // The complexContent element signals that we intend to restrict or extend // the content model of a complex type. -func (t *ComplexType) parseComplexContent(ns string, root *xmltree.Element) { +func (t *ComplexType) parseComplexContent(ns string, efd FormOption, afd FormOption, root *xmltree.Element) { var doc annotation if mixed := root.Attr("", "mixed"); mixed != "" { t.Mixed = parseBool(mixed) @@ -714,7 +730,7 @@ func (t *ComplexType) parseComplexContent(ns string, root *xmltree.Element) { usedElt := make(map[xml.Name]int) for _, v := range el.Search(schemaNS, "element") { - elt := parseElement(ns, v) + elt := parseElement(ns, efd, afd, v) if existing, ok := usedElt[elt.Name]; !ok { usedElt[elt.Name] = len(t.Elements) t.Elements = append(t.Elements, elt) @@ -724,7 +740,7 @@ func (t *ComplexType) parseComplexContent(ns string, root *xmltree.Element) { } for _, v := range el.Search(schemaNS, "attribute") { - t.Attributes = append(t.Attributes, parseAttribute(ns, v)) + t.Attributes = append(t.Attributes, parseAttribute(ns, afd, v)) } case "annotation": doc = doc.append(parseAnnotation(el)) @@ -814,10 +830,11 @@ func parseAnyElement(ns string, el *xmltree.Element) Element { } } -func parseElement(ns string, el *xmltree.Element) Element { +func parseElement(ns string, efd FormOption, afd FormOption, el *xmltree.Element) Element { var doc annotation e := Element{ Name: el.ResolveDefault(el.Attr("", "name"), ns), + Form: parseForm(el.Attr("", "form"), efd), Type: parseType(el.Resolve(el.Attr("", "type"))), Default: el.Attr("", "default"), Abstract: parseBool(el.Attr("", "abstract")), @@ -838,16 +855,12 @@ func parseElement(ns string, el *xmltree.Element) Element { doc = doc.append(parseAnnotation(el)) } }) - t, ok := e.Type.(linkedType) - if ok { - e.Name.Space = t.Space - } e.Doc = string(doc) e.Attr = el.StartElement.Attr return e } -func parseAttribute(ns string, el *xmltree.Element) Attribute { +func parseAttribute(ns string, afd FormOption, el *xmltree.Element) Attribute { var a Attribute var doc annotation // Non-QName xml attributes explicitly do *not* have a namespace. @@ -855,8 +868,9 @@ func parseAttribute(ns string, el *xmltree.Element) Attribute { a.Name = el.Resolve(el.Attr("", "name")) } else { a.Name.Local = name + a.Name.Space = ns } - a.Name.Space = ns + a.Form = parseForm(el.Attr("", "form"), afd) a.Type = parseType(el.Resolve(el.Attr("", "type"))) a.Default = el.Attr("", "default") a.Scope = el.Scope @@ -1107,3 +1121,15 @@ func (s *Schema) lookupType(name linkedType, ext map[xml.Name]Type) (Type, bool) v, ok := s.Types[xml.Name(name)] return v, ok } + +func parseForm(s string, d FormOption) FormOption { + s = strings.TrimSpace(s) + form := FormOption(s) + + if form == FormOptionUndefined { + return d + } else { + return form + } + +} diff --git a/xsd/testdata/AttributeGroup.xsd b/xsd/testdata/AttributeGroup.xsd index 147cfa4..49a8e58 100644 --- a/xsd/testdata/AttributeGroup.xsd +++ b/xsd/testdata/AttributeGroup.xsd @@ -9,7 +9,7 @@ - + - + diff --git a/xsd/testdata/ComplexType.json b/xsd/testdata/ComplexType.json index 33e2e3c..18a397e 100644 --- a/xsd/testdata/ComplexType.json +++ b/xsd/testdata/ComplexType.json @@ -1,27 +1,27 @@ { "CustomerType": { "Elements": [ - {"Name": {"Local": "CompanyName"}, "Type": 38}, - {"Name": {"Local": "ContactName"}, "Type": 38}, - {"Name": {"Local": "ContactTitle"}, "Type": 38}, - {"Name": {"Local": "Phone"}, "Type": 38}, - {"Name": {"Local": "Fax"}, "Type": 38}, + {"Name": {"Local": "CompanyName", "Space": "tns"}, "Type": 38}, + {"Name": {"Local": "ContactName", "Space": "tns"}, "Type": 38}, + {"Name": {"Local": "ContactTitle", "Space": "tns"}, "Type": 38}, + {"Name": {"Local": "Phone", "Space": "tns"}, "Type": 38}, + {"Name": {"Local": "Fax", "Space": "tns"}, "Type": 38}, { - "Name": {"Local": "FullAddress"}, + "Name": {"Local": "FullAddress", "Space": "tns"}, "Type": { "Elements": [ - {"Name": {"Local": "Address"}, "Type": 38}, - {"Name": {"Local": "City"}, "Type": 38}, - {"Name": {"Local": "Region"}, "Type": 38}, - {"Name": {"Local": "PostalCode"}, "Type": 38}, - {"Name": {"Local": "Country"}, "Type": 38} + {"Name": {"Local": "Address", "Space": "tns"}, "Type": 38}, + {"Name": {"Local": "City", "Space": "tns"}, "Type": 38}, + {"Name": {"Local": "Region", "Space": "tns"}, "Type": 38}, + {"Name": {"Local": "PostalCode", "Space": "tns"}, "Type": 38}, + {"Name": {"Local": "Country", "Space": "tns"}, "Type": 38} ], - "Attributes": [{"Name": {"Local": "CustomerID"}, "Type": 40}] + "Attributes": [{"Name": {"Local": "CustomerID", "Space": "tns"}, "Type": 40}] } } ], "Attributes": [ - {"Name": {"Local": "CustomerID"}, "Type": 40} + {"Name": {"Local": "CustomerID", "Space": "tns"}, "Type": 40} ] } } diff --git a/xsd/testdata/ComplexType.xsd b/xsd/testdata/ComplexType.xsd index dcf9ce8..6d034cc 100644 --- a/xsd/testdata/ComplexType.xsd +++ b/xsd/testdata/ComplexType.xsd @@ -5,13 +5,13 @@ - + - - + + @@ -21,4 +21,3 @@ - diff --git a/xsd/xsd.go b/xsd/xsd.go index fb84c36..0f11f09 100644 --- a/xsd/xsd.go +++ b/xsd/xsd.go @@ -49,6 +49,8 @@ type Element struct { Doc string // The canonical name of this element Name xml.Name + // What form of the naming to expect + Form FormOption // True if this element can have any name. See // http://www.w3.org/TR/2004/REC-xmlschema-1-20041028/structures.html#element-any Wildcard bool @@ -80,6 +82,8 @@ type Attribute struct { // The canonical name of this attribute. It is uncommon for attributes // to have a name space. Name xml.Name + // What form of the naming to expect + Form FormOption // Annotation provided for this attribute by the schema author. Doc string // The type of the attribute value. Must be a simple or built-in Type. @@ -103,6 +107,10 @@ type Schema struct { // The Target namespace of the schema. All types defined in this // schema will be in this name space. TargetNS string `xml:"targetNamespace,attr"` + // ElementFormDefault + ElementFormDefault FormOption `xml:"elementFormDefault,attr,omitempty"` + // AttributeFormDefault + AttributeFormDefault FormOption `xml:"attributeFormDefault,attr,omitempty"` // Types defined in this schema declaration Types map[xml.Name]Type // Any annotations declared at the top-level of the schema, separated @@ -329,3 +337,11 @@ var StandardSchema = [][]byte{ wsdl2003xsd, // http://schemas.xmlsoap.org/wsdl/ xlinkxsd, // http://www.w3.org/1999/xlink } + +type FormOption string + +const ( + FormOptionUndefined FormOption = "" + FormOptionUnqualified FormOption = "unqualified" + FormOptionQualified FormOption = "qualified" +) diff --git a/xsd/xsd_test.go b/xsd/xsd_test.go index f79ecb6..060f07d 100644 --- a/xsd/xsd_test.go +++ b/xsd/xsd_test.go @@ -131,6 +131,7 @@ func testCompare(t *testing.T, prefix []string, got, want interface{}) bool { } if got != want { t.Errorf("%s: got %#v, wanted %#v", path, got, want) + return false } return true } diff --git a/xsdgen/cli.go b/xsdgen/cli.go index 0274086..3cdf641 100644 --- a/xsdgen/cli.go +++ b/xsdgen/cli.go @@ -6,6 +6,7 @@ import ( "fmt" "go/ast" "io/ioutil" + "path/filepath" "strings" "aqwari.net/xml/internal/commandline" @@ -62,22 +63,31 @@ func (cfg *Config) GenAST(files ...string) (*ast.File, error) { return code.GenAST() } -func (cfg *Config) readFiles(files ...string) ([][]byte,error) { +func (cfg *Config) readFiles(files ...string) ([][]byte, error) { data := make([][]byte, 0, len(files)) for _, filename := range files { - b, err := ioutil.ReadFile(filename) + path, err := filepath.Abs(filename) if err != nil { return nil, err } - cfg.debugf("read %s", filename) + b, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + cfg.debugf("read %s(%s)", path, filename) if cfg.followImports { + dir := filepath.Dir(path) importedRefs, err := xsd.Imports(b) if err != nil { return nil, fmt.Errorf("error discovering imports: %v", err) } importedFiles := make([]string, 0, len(importedRefs)) for _, r := range importedRefs { - importedFiles = append(importedFiles, r.Location) + if filepath.IsAbs(r.Location) { + importedFiles = append(importedFiles, r.Location) + } else { + importedFiles = append(importedFiles, filepath.Join(dir, r.Location)) + } } referencedData, err := cfg.readFiles(importedFiles...) if err != nil { @@ -110,15 +120,16 @@ func (cfg *Config) GenSource(files ...string) ([]byte, error) { // same as those passed to the xsdgen command. func (cfg *Config) GenCLI(arguments ...string) error { var ( - err error - replaceRules commandline.ReplaceRuleList - xmlns commandline.Strings - fs = flag.NewFlagSet("xsdgen", flag.ExitOnError) - packageName = fs.String("pkg", "", "name of the the generated package") - output = fs.String("o", "xsdgen_output.go", "name of the output file") - followImports = fs.Bool("f", false, "follow import statements; load imported references recursively into scope") - verbose = fs.Bool("v", false, "print verbose output") - debug = fs.Bool("vv", false, "print debug output") + err error + replaceRules commandline.ReplaceRuleList + xmlns commandline.Strings + fs = flag.NewFlagSet("xsdgen", flag.ExitOnError) + packageName = fs.String("pkg", "", "name of the the generated package") + output = fs.String("o", "xsdgen_output.go", "name of the output file") + followImports = fs.Bool("f", false, "follow import statements; load imported references recursively into scope") + targetNamespacesOnly = fs.Bool("t", false, "restict output of types to these declared in the target namespace(s) provided") + verbose = fs.Bool("v", false, "print verbose output") + debug = fs.Bool("vv", false, "print debug output") ) fs.Var(&replaceRules, "r", "replacement rule 'regex -> repl' (can be used multiple times)") fs.Var(&xmlns, "ns", "target namespace(s) to generate types for") @@ -136,6 +147,7 @@ func (cfg *Config) GenCLI(arguments ...string) error { } cfg.Option(Namespaces(xmlns...)) cfg.Option(FollowImports(*followImports)) + cfg.Option(TargetNamespacesOnly(*targetNamespacesOnly)) for _, r := range replaceRules { cfg.Option(replaceAllNamesRegex(r.From, r.To)) } diff --git a/xsdgen/config.go b/xsdgen/config.go index a2bf601..9d8cbe1 100644 --- a/xsdgen/config.go +++ b/xsdgen/config.go @@ -21,9 +21,10 @@ type Config struct { namespaces []string pkgname string // load xsd imports recursively into memory before parsing - followImports bool - preprocessType typeTransform - postprocessType specTransform + followImports bool + targetNamespacesOnly bool + preprocessType typeTransform + postprocessType specTransform // Helper functions helperFuncs map[string]*ast.FuncDecl helperTypes map[xml.Name]spec @@ -247,6 +248,18 @@ func FollowImports(follow bool) Option { } } +// TargetNamespacesOnly specifies whether or +// not to filter +// to recursively read in imported schemas +// before attempting to parse +func TargetNamespacesOnly(tnsOnly bool) Option { + return func(cfg *Config) Option { + prev := cfg.targetNamespacesOnly + cfg.targetNamespacesOnly = tnsOnly + return TargetNamespacesOnly(prev) + } +} + // Replace allows for substitution rules for all identifiers to // be specified. If an invalid regular expression is called, no action // is taken. The Replace option is additive; subsitutions will be diff --git a/xsdgen/example_test.go b/xsdgen/example_test.go index 3e3e004..7948407 100644 --- a/xsdgen/example_test.go +++ b/xsdgen/example_test.go @@ -109,8 +109,8 @@ func ExampleIgnoreElements() { // package ws // // type Person struct { - // Name string `xml:"http://www.example.com/ name"` - // Deceased bool `xml:"http://schemas.xmlsoap.org/soap/encoding/ deceased"` + // Name string `xml:"name"` + // Deceased bool `xml:"deceased"` // } } @@ -135,7 +135,7 @@ func ExamplePackageName() { // // package postal // - // // May be no more than 10 items long + // // Must be exactly 10 items long // type Zipcode string } @@ -190,7 +190,7 @@ func ExampleHandleSOAPArrayType() { // // type BoolArray struct { // Items []bool `xml:",any"` - // Offset string `xml:"offset,attr,omitempty"` + // Offset string `xml:"http://schemas.xmlsoap.org/soap/encoding/ offset,attr,omitempty"` // Id string `xml:"id,attr,omitempty"` // Href string `xml:"href,attr,omitempty"` // } @@ -291,34 +291,34 @@ func ExampleUseFieldNames() { // ) // // type Book struct { - // Title string `xml:"http://www.example.com/ title"` - // Published time.Time `xml:"http://www.example.com/ published"` - // Author string `xml:"http://www.example.com/ author"` + // Title string `xml:"title"` + // Published time.Time `xml:"published"` + // Author string `xml:"author"` // } // - // func (t *Book) MarshalXML(e *xml.Encoder, start xml.StartElement) error { - // type T Book - // var layout struct { - // *T - // Published *xsdDate `xml:"http://www.example.com/ published"` - // } - // layout.T = (*T)(t) - // layout.Published = (*xsdDate)(&layout.T.Published) - // return e.EncodeElement(layout, start) - // } - // func (t *Book) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { - // type T Book - // var overlay struct { - // *T - // Published *xsdDate `xml:"http://www.example.com/ published"` - // } - // overlay.T = (*T)(t) - // overlay.Published = (*xsdDate)(&overlay.T.Published) - // return d.DecodeElement(&overlay, &start) - // } + //func (t *Book) MarshalXML(e *xml.Encoder, start xml.StartElement) error { + // type T Book + // var layout struct { + // *T + // Published *xsdDate `xml:"published"` + // } + // layout.T = (*T)(t) + // layout.Published = (*xsdDate)(&layout.T.Published) + // return e.EncodeElement(layout, start) + //} + //func (t *Book) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + // type T Book + // var overlay struct { + // *T + // Published *xsdDate `xml:"published"` + // } + // overlay.T = (*T)(t) + // overlay.Published = (*xsdDate)(&overlay.T.Published) + // return d.DecodeElement(&overlay, &start) + //} // // type Library struct { - // Book []Book `xml:"http://www.example.com/ book"` + // Book []Book `xml:"book"` // } // // type xsdDate time.Time diff --git a/xsdgen/testdata/common.xsd b/xsdgen/testdata/common.xsd new file mode 100644 index 0000000..1b1e63e --- /dev/null +++ b/xsdgen/testdata/common.xsd @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/xsdgen/testdata/ns1.xsd b/xsdgen/testdata/ns1.xsd new file mode 100644 index 0000000..5007ecb --- /dev/null +++ b/xsdgen/testdata/ns1.xsd @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/xsdgen/testdata/ns2.xsd b/xsdgen/testdata/ns2.xsd new file mode 100644 index 0000000..b3502b3 --- /dev/null +++ b/xsdgen/testdata/ns2.xsd @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/xsdgen/xsdgen.go b/xsdgen/xsdgen.go index 0604c4d..f3fbce4 100644 --- a/xsdgen/xsdgen.go +++ b/xsdgen/xsdgen.go @@ -463,6 +463,10 @@ func (cfg *Config) flatten1(t xsd.Type, push func(xsd.Type), depth int) xsd.Type t.Doc = "Must be at least " + strconv.Itoa(t.Restriction.MinLength) + " items long" return t } + if t.Restriction.Length != 0 { + t.Doc = "Must be exactly " + strconv.Itoa(t.Restriction.Length) + " items long" + return t + } return t.Base case *xsd.ComplexType: // We can "unpack" a struct if it is extending a simple @@ -524,7 +528,21 @@ func (cfg *Config) flatten1(t xsd.Type, push func(xsd.Type), depth int) xsd.Type func (cfg *Config) genTypeSpec(t xsd.Type) (result []spec, err error) { var s []spec - cfg.debugf("generating type spec for %q", xsd.XMLName(t).Local) + + name := xsd.XMLName(t) + if cfg.targetNamespacesOnly { + found := false + for _, ns := range cfg.namespaces { + if ns == name.Space { + found = true + } + } + if !found { + return result, nil + } + } + + cfg.debugf("generating type spec for %q", name.Local) switch t := t.(type) { case *xsd.SimpleType: @@ -534,7 +552,7 @@ func (cfg *Config) genTypeSpec(t xsd.Type) (result []spec, err error) { case xsd.Builtin: // pass default: - cfg.logf("unexpected %T %s", t, xsd.XMLName(t).Local) + cfg.logf("unexpected %T %s", t, name.Local) } if err != nil || s == nil { return result, err @@ -684,7 +702,14 @@ func (cfg *Config) genComplexType(t *xsd.ComplexType) ([]spec, error) { if el.Nillable || el.Optional { options = ",omitempty" } - tag := fmt.Sprintf(`xml:"%s %s%s"`, el.Name.Space, el.Name.Local, options) + + tag := "" + if el.Form == xsd.FormOptionQualified || t.Name.Space != el.Name.Space { + tag = fmt.Sprintf(`xml:"%s %s%s"`, el.Name.Space, el.Name.Local, options) + } else { + tag = fmt.Sprintf(`xml:"%s%s"`, el.Name.Local, options) + } + base, err := cfg.expr(el.Type) if err != nil { return nil, fmt.Errorf("%s element %s: %v", t.Name.Local, el.Name.Local, err) @@ -731,14 +756,8 @@ func (cfg *Config) genComplexType(t *xsd.ComplexType) ([]spec, error) { if attr.Optional { options = ",omitempty" } - qualified := false - for _, attrAttr := range attr.Attr { - if attrAttr.Name.Space == "" && attrAttr.Name.Local == "form" && attrAttr.Value == "qualified" { - qualified = true - } - } var tag string - if qualified { + if attr.Form == xsd.FormOptionQualified { tag = fmt.Sprintf(`xml:"%s %s,attr%s"`, attr.Name.Space, attr.Name.Local, options) } else { tag = fmt.Sprintf(`xml:"%s,attr%s"`, attr.Name.Local, options) diff --git a/xsdgen/xsdgen_test.go b/xsdgen/xsdgen_test.go index d206de7..118bde3 100644 --- a/xsdgen/xsdgen_test.go +++ b/xsdgen/xsdgen_test.go @@ -47,7 +47,7 @@ func testGen(t *testing.T, ns string, files ...string) string { cfg.Option(DefaultOptions...) cfg.Option(LogOutput((*testLogger)(t))) - args := []string{"-v", "-o", file.Name(), "-ns", ns} + args := []string{"-v", "-o", file.Name(), "-ns", ns, "-f"} err = cfg.GenCLI(append(args, files...)...) if err != nil { t.Error(err) @@ -75,3 +75,7 @@ func TestBase64Binary(t *testing.T) { func TestSimpleUnion(t *testing.T) { t.Logf("%s\n", testGen(t, "http://example.org/", "testdata/simple-union.xsd")) } + +func TestImports(t *testing.T) { + t.Logf("%s\n", testGen(t, "ns1", "testdata/ns1.xsd")) +}