droyo / go-xml Goto Github PK
View Code? Open in Web Editor NEWutility and code-generation libraries for XML
License: MIT License
utility and code-generation libraries for XML
License: MIT License
Let's talk about the elephant in the room: references in SOAP documents. We can generate the greatest type declarations ever, but that won't do us any good in the face of documents such as this:
<Body>
<multiRef id="obj0" soapenc:root="0" xsi:type="myNS:Part">
<sku>SJ-47</sku>
</multiRef>
<response>
<arg href="#obj0"/>
</response>
</Body>
ugh. What's worse, one of the explicit reasons listed in the SOAP spec for references is to support data models that include loops. A loop would cause some problems if we simply tried to flatten a document.
A long time ago I wrote a tiny package that took the "flattening" approach: aqwari.net/exp/soap
. That's one approach.
What I am leaning towards is developing a standalone package that users can choose to add to their generated packages if they know they're going to be dealing with references. Something that wraps an xml.Decoder
and decodes a document as if it were flattened (while still handling loops in a sane way if possible). That package should have more stringent release engineering/compatibility guarantees, since it will be imported in users' code. But I haven't started this package, so I'm keeping an issue here so I don't forget.
Using xsdgen
to generate code from http://standards.iso.org/ittf/PubliclyAvailableStandards/MPEG-DASH_schema_files/DASH-MPD.xsd
Produces code such as the following:
type EventStreamType struct {
Actuate ActuateType `xml:"actuate,attr"`
SchemeIdUri string `xml:"schemeIdUri,attr"`
Value string `xml:"value,attr"`
Timescale uint `xml:"timescale,attr"`
Items []string `xml:",any"`
Event []EventType `xml:"urn:mpeg:dash:schema:mpd:2011 Event"`
}
Without including the declaration for ActuateType
. Here's the relevant part of the XSD:
<xs:complexType name="EventStreamType">
<xs:sequence>
<xs:element name="Event" type="EventType" minOccurs="0" maxOccurs="unbounded"/>
<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute ref="xlink:href"/>
<xs:attribute ref="xlink:actuate" default="onRequest"/>
<xs:attribute name="schemeIdUri" type="xs:anyURI" use="required"/>
<xs:attribute name="value" type="xs:string"/>
<xs:attribute name="timescale" type="xs:unsignedInt"/>
</xs:complexType>
Notably, the actuate
attribute is actually pulled in as a reference to another XSD. The xsdgen
package should have known that its type declaration was necessary and produced it in the generated code.
Currently the MarshalXML and UnmarshalXML methods for types are stubs
The generated definition:
type base64Binary []byte
func (b base64Binary) UnmarshalText(text []byte) (err error) {
*b, err = base64.StdEncoding.DecodeString(string(text))
return err
}
func (b base64Binary) MarshalText() ([]byte, error) {
var buf bytes.Buffer
enc := base64.NewEncoder(base64.StdEncoding, &buf)
enc.Write([]byte(b))
enc.Close()
return buf.Bytes()
}
There are 3 problems:
*b, err = base64.StdEncoding.DecodeString(string(text))
failed as invalid indirect of b (type base64Binary)
MarshalText
failed as not enough arguments to return
The corresponding fields actually are not in base64Binary
type.
From the xsd specification http://www.fdsn.org/xml/station/fdsn-station-1.0.xsd, the "Root element" defined:
<!-- Root element -->
<xs:element name="FDSNStationXML" type="fsx:RootType"/>
<!-- Type definitions -->
<xs:complexType name="RootType">
<xs:annotation>
...
Which means the root element struct name should be "FDSNStationXML", e.g.:
type FDSNStationXML struct {
SchemaVersion float64 `xml:"schemaVersion,attr"`
...
However, currently xsdgen's output is:
type RootType struct {
SchemaVersion float64 `xml:"schemaVersion,attr"`
...
This problem only happens in the root element.
The xsdgen package flattens the XSD type hierarchy to produce nicer code.
a → b→ c → anyType
becomes
a → anyType
b → anyType
c → anyType
Practically speaking, instead of generating code like this:
type MyString string
type AllCapitalString MyString
type ShortAllCapitalString AllCapitalString
It generates code like this:
type MyString string
type AllCapitalString string
type ShortAllCapitalString string
This is done in the Config.flatten method. There are so many problems with this function, I don't know where to start.
The cfg.flatten
and cfg.flatten1
functions need a redo, with the following goals:
internal/dependency
package's Graph
type to model dependencies between derived types and the types of their attributes, elements and ancestors, to avoid missing indirectly required types.When I attempt to follow the instructions in the README.md
I get the following.
~/src/github.com/droyo/go-xml/gentests [master]$ go generate
_testgen/testgen.go:14:2: use of internal package aqwari.net/xml/internal/gen not allowed
gentest.go:5: running "go": exit status 1
~/src/github.com/droyo/go-xml/gentests [master]$ git rev-parse HEAD
d48ea63e5a613b3e7b7360e2d0f3476ea77c263d
From following the godoc, it appears that this is because this package is in fact aqwari.net/xml, though this isn't noted in the README here, and there is no import comment restricting incorrect imports.
The certificate for aqwari.net expired Thursday, 30 January 2020 at 13:42:38 this causes the following failure on go get. (https fetch: Get https://aqwari.net/xml?go-get=1: x509: certificate has expired or is not yet valid
When trying to generate the schema structures for the Tableau 3.6. api, the generation is invalid.
Full xsd is available here: https://help.tableau.com/samples/en-us/rest_api/ts-api_3_6.xsd
More specifically the Lastday in this monthDay attribute is generated as 'type string', while it should have been 'type LastDay string'
<xs:complexType name="intervalType">
<xs:attribute name="minutes">
<xs:simpleType>
<xs:restriction base="xs:nonNegativeInteger">
<xs:enumeration value="15" />
<xs:enumeration value="30" />
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="hours">
<xs:simpleType>
<xs:restriction base="xs:nonNegativeInteger">
<xs:enumeration value="1" />
<xs:enumeration value="2" />
<xs:enumeration value="4" />
<xs:enumeration value="6" />
<xs:enumeration value="8" />
<xs:enumeration value="12" />
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="weekDay" >
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="Monday" />
<xs:enumeration value="Tuesday" />
<xs:enumeration value="Wednesday" />
<xs:enumeration value="Thursday" />
<xs:enumeration value="Friday" />
<xs:enumeration value="Saturday" />
<xs:enumeration value="Sunday" />
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="monthDay" >
<xs:simpleType>
<xs:union>
<xs:simpleType>
<xs:restriction base="xs:integer">
<xs:minInclusive value="1" />
<xs:maxInclusive value="31" />
</xs:restriction>
</xs:simpleType>
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="LastDay" />
</xs:restriction>
</xs:simpleType>
</xs:union>
</xs:simpleType>
</xs:attribute>
</xs:complexType>
I suspect a problem with the union combining 2 different datatypes 'xs:integer' and 'xs:string', but I have not investigated in detail. Other union combining 'xs:integer' with 'xs:integer' works fine.
Currently using this workaround, as there seems to be only 1 xsdSimpleType with a blanc name:
func main() {
var cfg xsdgen.Config
cfg.Option(
xsdgen.IgnoreAttributes("href", "offset"),
xsdgen.Replace(`[._ \s-]`, ""),
xsdgen.PackageName("ws"),
xsdgen.HandleSOAPArrayType(),
xsdgen.SOAPArrayAsSlice(),
xsdgen.UseFieldNames(),
xsdgen.ProcessTypes(func(s xsd.Schema, t xsd.Type) xsd.Type {
switch t.(type) {
case *xsd.SimpleType:
// workaround for the invalid generation of LastDay type,
st := t.(*xsd.SimpleType)
if st.Name.Local == "" {
st.Name.Local = "LastDay"
}
}
return t
}),
)
if err := cfg.GenCLI(os.Args[1:]...); err != nil {
log.Fatal(err)
}
}
Right at the bottom of http://schemas.xmlsoap.org/soap/encoding/ is the line
<xs:element name="anyType"/>
What should we do with that? What does that even mean? What is the correct type of this element? The xsd package will currently give the error
could not find type "" in namespace "" for element "anyType"
Obviously, this is in a W3C-authored XSD, so it must be valid(right?). We'll need to be able to handle such types. From the name, it sounds like a type that can contain anything. One approach would be making xs:anyType
the default type for all elements.
Hi, I am trying to include the external xmldsig schema into my schema and to generate the structs. This steps works and this is great!
In the next step I use xmlsec1 to sign the marshalled XML but then it complains that it can't find the Signature node.
Error: failed to find default node with name="Signature"
It seems that xmlsec1 expects that the marshalled XML contains a Signaute node with its explicit xmlns namespace/
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
When I am marshalling the XML I get a Signature node without its namespace but all its child nodes contain it. Is it somehow possible to achieve that the Signature node also has its namespace explicitly when marshalled?
Result:
<MetaDataType>
<Signature>
<SignedInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<CanonicalizationMethod xmlns="http://www.w3.org/2000/09/xmldsig#" Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
<SignatureMethod xmlns="http://www.w3.org/2000/09/xmldsig#" Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<Reference xmlns="http://www.w3.org/2000/09/xmldsig#">
<DigestMethod xmlns="http://www.w3.org/2000/09/xmldsig#" Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<DigestValue xmlns="http://www.w3.org/2000/09/xmldsig#"/>
</Reference>
</SignedInfo>
<SignatureValue xmlns="http://www.w3.org/2000/09/xmldsig#"/>
</Signature>
</MetaDataType>
Schema:
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
elementFormDefault="qualified">
<xs:import namespace="http://www.w3.org/2000/09/xmldsig#"
schemaLocation="xmldsig-core-schema.xsd" />
<xs:element name="MetaData" type="MetaDataType" />
<xs:complexType name="MetaDataType">
<xs:sequence>
<xs:element ref="ds:Signature"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
Generated code:
type MetaDataType struct {
Signature *SignatureType `xml:" Signature"`
}
type SignatureType struct {
SignedInfo *SignedInfoType `xml:"http://www.w3.org/2000/09/xmldsig# SignedInfo"`
SignatureValue *SignatureValueType `xml:"http://www.w3.org/2000/09/xmldsig# SignatureValue"`
KeyInfo *KeyInfoType `xml:"http://www.w3.org/2000/09/xmldsig# KeyInfo,omitempty"`
Object []ObjectType `xml:"http://www.w3.org/2000/09/xmldsig# Object,omitempty"`
Id string `xml:"Id,attr,omitempty"`
}
Hi,
I've used xsdgen command line to generate xsd from http://www.fdsn.org/xml/station/1 .
However, the generated go skipped the base type for xs:restriction.
For example, if you look into the generated go struct of "Longitude"->"LongitudeType"->"LongitudeBaseType" you'll see there's no "FloatType" field:
type LatitudeBaseType struct {
Unit string `xml:"unit,attr"`
PlusError float64 `xml:"plusError,attr"`
MinusError float64 `xml:"minusError,attr"`
}
, but the xsd of LongitudeBaseType is:
<xs:complexType name="LongitudeBaseType">
<xs:simpleContent>
<xs:restriction base="fsx:FloatType">
<xs:minInclusive value="-180"/>
<xs:maxInclusive value="180"/>
<xs:attribute name="unit" type="xs:string" use="optional" fixed="DEGREES"/>
<xs:attributeGroup ref="fsx:uncertaintyDouble"/>
</xs:restriction>
</xs:simpleContent>
</xs:complexType>
I've misread the XML schema spec. "fixed" has nothing to do with overriding the value of an attribute, and everything to do with preventing derivations of a type from changing the value.
Since the "fixed" attribute is only useful for schema authoring tools and validation tools, it should be dropped completely from the xsd package.
When developing new features and fixing bugs, progress is impeded by the lack of thorough automated tests. There are tests for each package that verify xsd files can be parsed without errors, and that Go code can be generated without errors. However, there is not enough tests of the generated code itself.
Develop unit tests that generate Go code for XSD fragments and check the following:
The tests should be data-driven, so it is easy to add additional test cases just by providing an XSD and some sample data. In the future the tests can be extended to ensure default values are set (see #2), but that is not a requirement for this issue.
encoding/xml
's handling of XML namespaces, so a simple equivalency ==
check won't be enough.The dash
branch contains the DASH-MPD XSD and sample data in the gentest
directory.
Currently the tests fail due to an invalid marshalling of the SegmentList
element. It is supposed to generate something like this:
<SegmentList timescale="90000" duration="5400000">
<RepresentationIndex sourceURL="representation-index.sidx">
</RepresentationIndex>
<SegmentURL media="segment-1.ts">
</SegmentURL>
...
</SegmentList>
Instead we get this:
<SegmentList timescale="90000" xmlns="urn:mpeg:dash:schema:mpd:2011">
<Items>
</Items>
<Items>
</Items>
...
</SegmentList>
This is due to the <SegmentURL>
items being unmarshalled into the wrong place. Here's the the relevant type declarations:
type SegmentListType struct {
MultipleSegmentBaseType
Actuate ActuateType `xml:"actuate,attr,omitempty"`
SegmentURL []SegmentURLType `xml:"urn:mpeg:dash:schema:mpd:2011 SegmentURL,omitempty"`
}
Within MultipleSegmentBaseType is the following:
Items []string `xml:",any"`
This feels like a possible bug, or at least, unexpected behavior in the encoding/xml
package; it is dropping the SegmentURL
items into this wildcard field, even though further down SegmentListType
there is a more specific field matching those items. This is probably due to the fact that the wildcard field is in an embedded type.
Thank you for a great library! It's been really helpful for me to use while traversing XML.
I'm using the library to marshal XML from xmltree.Element
. The XML generated will in some cases be read by a human and because of that I'm using xmltree.MarshalIndent
. The problem is that the XML generated includes too much leading and trailing whitespaces so when being unmarshalled back to the original type (with standard library) the content includes unwanted newlines.
Example to reproduce
package main
import (
"encoding/xml"
"fmt"
"aqwari.net/xml/xmltree"
)
type Test struct {
Field1 string `xml:"parent>child1"`
Field2 int `xml:"parent>child2"`
Field3 string `xml:"parent>child3"`
}
func main() {
t := Test{"test string", 10, "another string"}
// MarshalIndent with standard library
std, _ := xml.MarshalIndent(t, "", " ")
// Parse the mashalled XML with xml tree and MarshalIndent the parsed tree
elements, _ := xmltree.Parse(std)
xt := xmltree.MarshalIndent(elements, "", " ")
// Print result from both libraries
fmt.Println(string(std))
fmt.Println("-")
fmt.Println(string(xt))
// Unmarshal the two generated byte slices with standard library
t1 := Test{}
t2 := Test{}
xml.Unmarshal(std, &t1)
xml.Unmarshal(xt, &t2)
// The XML from stdandard library contains no whitespaces
fmt.Printf(
"Stdlib: f1 '%s', f2 '%d', f3: '%s'\n",
t1.Field1, t1.Field2, t1.Field3,
)
// The XML from xmltree library contains too much whitespaces
fmt.Printf(
"Xmltree: f1 '%s', f2 '%d', f3: '%s'\n",
t2.Field1, t2.Field2, t2.Field3,
)
}
Output
<Test>
<parent>
<child1>test string</child1>
<child2>10</child2>
<child3>another string</child3>
</parent>
</Test>
-
<Test>
<parent>
<child1>
test string
</child1>
<child2>
10
</child2>
<child3>
another string
</child3>
</parent>
</Test>
Stdlib: f1 'test string', f2 '10', f3: 'another string'
Xmltree: f1 '
test string
', f2 '10', f3: '
another string
'
This change to xmltree/marshal.go
seems to fix my issues:
diff --git a/xmltree/marshal.go b/xmltree/marshal.go
index 001b63a..1c12495 100644
--- a/xmltree/marshal.go
+++ b/xmltree/marshal.go
@@ -115,16 +115,8 @@ func (e *encoder) encode(el, parent *Element, visited map[*Element]struct{}) err
}
func (e *encoder) WriteContent(content []byte, depth int) error {
- if e.pretty {
- io.WriteString(e.w, e.prefix)
- for i := 0; i < depth; i++ {
- io.WriteString(e.w, e.indent)
- }
- }
e.w.Write(content)
- if e.pretty && !bytes.HasSuffix(content, []byte("\n")) {
- io.WriteString(e.w, "\n")
- }
+
return nil
}
@@ -161,7 +153,9 @@ func (e *encoder) encodeOpenTag(el *Element, scope Scope, depth int) error {
return err
}
if e.pretty {
- io.WriteString(e.w, "\n")
+ if len(el.Children) > 0 || len(el.Content) == 0 {
+ io.WriteString(e.w, "\n")
+ }
}
return nil
}
@@ -169,7 +163,9 @@ func (e *encoder) encodeOpenTag(el *Element, scope Scope, depth int) error {
func (e *encoder) encodeCloseTag(el *Element, depth int) error {
if e.pretty {
for i := 0; i < depth; i++ {
- io.WriteString(e.w, e.indent)
+ if len(el.Children) > 0 {
+ io.WriteString(e.w, e.indent)
+ }
}
}
if err := tagTmpl.ExecuteTemplate(e.w, "end", el); err != nil {
So while running the example again with the above changes this is the output:
<Test>
<parent>
<child1>test string</child1>
<child2>10</child2>
<child3>another string</child3>
</parent>
</Test>
-
<Test>
<parent>
<child1>test string</child1>
<child2>10</child2>
<child3>another string</child3>
</parent>
</Test>
Stdlib: f1 'test string', f2 '10', f3: 'another string'
Xmltree: f1 'test string', f2 '10', f3: 'another string'
Am I missing something or do you agree that this is a bug? If you agree, do you want me to create a PR?
Thanks!
We need a workaround for https://golang.org/issues/11939. Empty structs (full of zeroes) for which ,omitempty is present on all field tags should not show up in MarshalXML output, but they do. See https://play.golang.org/p/J-_l2JySA0 for a trivial example of the problem.
One solution would be to use pointers for everything. However, I don't want to do that, because it makes the resulting types more difficult to use, as each field access will require if v.FieldName != nil { ...
guards.
Another solution would be to make MarshalXML methods smarter. Ignoring struct embedding which will be gone when #31 is resolved, we can skip emitting the complexType T
if the following conditions are satisfied:
T
's fields use the ,omitempty
optionT
's fields are the zero value for the field's typeThe second condition is kind of annoying to check. Something like this would cover most cases:
if (v == T{})
but it won't compile if T
contains a slice, such as with list
types or base64Binary
derivatives.
When a SimpleType without a base type is processed in flatten1(), the debugf() on line xsdgen/xsdgen.go:280 causes xsdgen to crash - calling XMLName(nil) will trigger the panic() in xsd/xsd.go:293.
Since this is only used for debugging, it's probably a good idea to remove the panic() completely or guard this with a check:
if builtin == nil {
cfg.debugf("%T(%s) has no base type, cannot transform", t, xsd.XMLName(t).Local)
} else {
cfg.debugf("Transforming %T(%s) into base type %T(%s)", t, xsd.XMLName(t).Local, t.Base, xsd.XMLName(t.Base).Local)
}
And before returning t.Base (which is nil), there should be something like this:
if t.Base == nil {
return t
}
I don't really understand why there is no base type in my particular test case, but handling this special case may be a good idea anyway?
For reference, this is the XSD I'm trying to process:
http://standards.iso.org/ittf/PubliclyAvailableStandards/MPEG-DASH_schema_files/DASH-MPD.xsd
And the type that it hits is a ConditionalUintType.
I try to generate structs for this xsd with this command xsdgen -o schema.go -pkg types -vv Types.xsd
and get panic:
*xsd.SimpleType(ordinalNumberText) -> xsd.Builtin(string)
panic: xsd: unexpected xsd.Type <nil> <nil> passed to XMLName
goroutine 1 [running]:
panic(0x1e03a0, 0xc420554c20)
/usr/local/Cellar/go/1.7.4_2/libexec/src/runtime/panic.go:500 +0x1a1
aqwari.net/xml/xsd.XMLName(0x0, 0x0, 0xc4200715f0, 0x23, 0xc4203daf80, 0x14)
./vendor/src/aqwari.net/xml/xsd/xsd.go:290 +0x240
aqwari.net/xml/xsdgen.(*Config).flatten1(0xc4200dc480, 0x3793c0, 0xc420130480, 0xc4205bd128, 0x1, 0xc420588230)
./vendor/src/aqwari.net/xml/xsdgen/xsdgen.go:344 +0x358
aqwari.net/xml/xsdgen.(*Config).flatten1(0xc4200dc480, 0x3793c0, 0xc42013e300, 0xc4205bd128, 0x3, 0x3)
./vendor/src/aqwari.net/xml/xsdgen/xsdgen.go:332 +0x200
aqwari.net/xml/xsdgen.(*Config).flatten1(0xc4200dc480, 0x3793c0, 0xc4203bb980, 0xc4205bd128, 0x2, 0x2)
./vendor/src/aqwari.net/xml/xsdgen/xsdgen.go:332 +0x200
aqwari.net/xml/xsdgen.(*Config).flatten(0xc4200dc480, 0xc4200d15c0, 0x27, 0xc420554840, 0x1)
./vendor/src/aqwari.net/xml/xsdgen/xsdgen.go:236 +0x286
aqwari.net/xml/xsdgen.(*Config).gen(0xc4200dc480, 0xc4203fc7e0, 0x1, 0x1, 0xc420556000, 0x5, 0x8, 0x0, 0x30, 0x34848efe00075a08)
./vendor/src/aqwari.net/xml/xsdgen/xsdgen.go:147 +0x6cf
aqwari.net/xml/xsdgen.(*Config).GenCode(0xc4200dc480, 0xc4200d6e80, 0x1, 0x1, 0x1, 0x1, 0x0)
./vendor/src/aqwari.net/xml/xsdgen/cli.go:41 +0x49b
Some XML schema, such as http://schema.bolagsverket.se/extern/foretagsinformation/F5_Rakenskapsinformation_2.00.xsd , make heavy use of imports. Add a tool, xsdfetch
,
that can be used to retrieve all the schema imported by a target schema.
Example usage:
xsdfetch url
xsdfetch filename
Some notes:
schemaLocation
field can be relative. In that case, we should treat it as relative to the targetNamespace
of the main schema. If that file is not found, we can then consider it relative to the URL of the main file (if we are fetching via URL).<import>
statements do not contain a schemaLocation
. One heuristic to find the schema file, in that case, is to append .xsd
to the schema's name space.schemaLocation
, there's not much we can do.<import>
and <include>
.All schema should be downloaded to the current directory, using path.Base(targetNS) + ".xsd"
as the file name, if unique.
I think this was introduced in #93 (I hit it merging #93 into my fork because I wanted optional structs to render as pointers).
Running xsdgen
throws the following error:
error generating go structs from xsd: oadr2b/oadr2b_tmp.go:1114:16: expected type, found '&' (and 5 more errors) in package oadr2b
In the debug output that follows, I see the following struct rendered:
type X509DataType struct {
Item string `xml:",any"`
X509IssuerSerial *X509IssuerSerialType `xml:"http://www.w3.org/2000/09/xmldsig# X509IssuerSerial,omitempty"`
X509SKI *&{%!s(token.Pos=0) <nil> byte} `xml:"http://www.w3.org/2000/09/xmldsig# X509SKI,omitempty"`
X509SubjectName string `xml:"http://www.w3.org/2000/09/xmldsig# X509SubjectName,omitempty"`
X509Certificate *&{%!s(token.Pos=0) <nil> byte} `xml:"http://www.w3.org/2000/09/xmldsig# X509Certificate,omitempty"`
X509CRL *&{%!s(token.Pos=0) <nil> byte} `xml:"http://www.w3.org/2000/09/xmldsig# X509CRL,omitempty"`
}
I'm looking at it because I'd like the feature, but you may want to revert. I'll try to add a minimal schema to xsdgen/testdata that replicates the issue in any case.
Currently, the xsd
package ignores top-level elements; if they contain type definitions, it will parse those types and toss the surrounding element. When the wsdlgen
package receives a WSDL definition with a message defined by an element
instead of a type
, it tries to find a type with the same name, which may not necessarily exist. The workaround can be seen in wsdl/wsdl.go#L110 .
The wsdlgen package should support top-level schema elements. This likely requires an extension to the xsd.Schema
type that collects such declarations.
Is it support xsd:extension element. If yes let us know how to use this. I've tried the cfg.GenSource(doc) xsdgen function seems not working.
xsdgen
does not ensure struct fields for complex types are named uniquely, so generated code for a type with an attribute and element of the same name will not compile. Using a simple xsd like this:
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://example.org/">
<complexType name="myType2">
<sequence>
<element name="title" type="string"/>
<attribute name="title" type="string"/>
</sequence>
</complexType>
</schema>
Generates this:
package ws
type MyType2 struct {
Title string `xml:"title,attr"`
Title string `xml:"http://example.org/ title"`
}
Which is not valid code. For the attribute/element case in the example, we can append "Attr" to the field name for the attribute. I can't think of other type declarations that would cause this problem, but it's something to be mindful of if we add more support for unions. The relevant portion of the XSD spec is here, I think.
Hello,
I am creating a server that needs to consume and response with soap XML. I currently have consuming soap xml working correctly with xmltree package but is it possible for support to be added to xmltree to generate XML from a struct? I can't use the internal encoding/xml
as it requires two structs to marshal and unmarshal which is unmaintainable for my project.
Thanks in advance.
When the xsdgen package encounters a struct field with a "fixed" value, it will leave it out of the Go type, because the value is fixed and should not be changed by the user. However, such types need to have the fixed value set in their MarshalXML function to be valid according to the XML schema. Either include fixed values in the Go source, or generate the necessary marshalling functions. This needs to play nice with the existing marshalling methods, if they exist for a type already.
When a complex type is empty, no base is set. This causes panics and unintended behavior elsewhere in the codebase.
example xml:
<xsd:complexType name="Get_Committee_Defintion_Request_CriteriaType">
<xsd:annotation>
<xsd:documentation>Committee Definition Request Criteria</xsd:documentation>
</xsd:annotation>
</xsd:complexType>
I believe a solution to this issue would be to assign a base of AnyType to empty complex types. @droyo Would you suggest anything else?
Hi, I am trying to use xsdgen with a large xsd file with several large dependencies.
I get this error after a few seconds.
runtime: goroutine stack exceeds 1000000000-byte limit
fatal error: stack overflow
runtime stack:
runtime.throw(0x7a70d4, 0xe)
/usr/local/go/src/runtime/panic.go:608 +0x72
runtime.newstack()
/usr/local/go/src/runtime/stack.go:1008 +0x729
runtime.morestack()
/usr/local/go/src/runtime/asm_amd64.s:429 +0x8f
goroutine 1 [running]:
aqwari.net/xml/xsd.or.func1(0xc000234200, 0x0)
/home/droker/Development/go/src/aqwari.net/xml/xsd/search.go:20 +0x92 fp=0xc02191a368 sp=0xc02191a360 pc=0x597de2
aqwari.net/xml/xsd.hasChild.func1(0xc0004c7300, 0x597db5)
/home/droker/Development/go/src/aqwari.net/xml/xsd/search.go:33 +0x85 fp=0xc02191a3a0 sp=0xc02191a368 pc=0x597e75
aqwari.net/xml/xmltree.(*Element).SearchFunc.func1(0xc0004c7300)
/home/droker/Development/go/src/aqwari.net/xml/xmltree/xmltree.go:327 +0x45 fp=0xc02191a400 sp=0xc02191a3a0 pc=0x565ad5
aqwari.net/xml/xmltree.(*Element).walk(0xc000234100, 0xc0419190e0, 0xc000234100, 0x0)
/home/droker/Development/go/src/aqwari.net/xml/xmltree/xmltree.go:287 +0x3b fp=0xc02191a420 sp=0xc02191a400 pc=0x56520b
aqwari.net/xml/xmltree.(*Element).SearchFunc.func1(0xc000234100)
/home/droker/Development/go/src/aqwari.net/xml/xmltree/xmltree.go:330 +0xa0 fp=0xc02191a480 sp=0xc02191a420 pc=0x565b30
aqwari.net/xml/xmltree.(*Element).walk(0xc000563800, 0xc0419190e0, 0xc000563800, 0x0)
/home/droker/Development/go/src/aqwari.net/xml/xmltree/xmltree.go:287 +0x3b fp=0xc02191a4a0 sp=0xc02191a480 pc=0x56520b
aqwari.net/xml/xmltree.(*Element).SearchFunc.func1(0xc000563800)..............
There is a huge stack trace.
Any ideas if there is anything I can do?
Thanks
Dean
The xmltree package uses xml.Decoder.InputOffset
to capture the content of XML tags when constructing the tree. This does not play well with xml.Decoder.CharsetReader
. Compared to any of the iso 8859-* encodings, a utf8 encoding of the same text is almost always longer than the source, and InputOffset's return value is the input offset into the translated utf8 text. So simply using CharsetReader
naively will likely cause a run-time panic when we hit a non-ascii character.
Fix xmltree to gracefully handle non-utf8 charsets, or expose some way for the user to handle them.
I have an xsd:sequence that has an xsd:dateTime in the 3rd position.
As you know, xsd:sequence is strictly positional.
The generated MarshalXML code declares a layout struct to handle the conversion from time.Time to *xsdDateTime with a new member with the same same (member shadowing)
Unfortunately the shadowing member is serialized at last position, so the generated XML is not validated against the original XSD file
I hope my explanation is clear enough, I am attaching XSD files I am using to reproduce this issue (Italian electronic invoice formats)
Elements and attributes that have default values should get those values set if they contain the zero value for their Go type after marshalling.
I'm trying to use xsdgen on the SAML 2.0 XSD definitions. Specifically, saml-schema-assertion-2.0.xsd
as found at:
https://docs.oasis-open.org/security/saml/v2.0/
I went through and manually downloaded the dependencies and ran:
xsdgen saml-schema-assertion-2.0.xsd xmldsig-core-schema.xsd xenc-schema.xsd
The result is that the process's memory starts ballooning until I had to kill it. On my system that was around 32GB of memory. When I ran it with the -vv
flag it appears to be going in circles processing the files over and over. I don't know enough about the tool to say if it's stuck in a loop or some sort of NP-complete hell.
Go version: go version go1.13.6 linux/amd64
In tracking down a validation error I've just discovered that XML attributes need a to specify namespace! I thought I knew XML pretty well, but this was news to me!
For example, in <Foo xmlns="http://bar/" hello="world" />
the hello
attribute does not have a namespace.
If we generated this strucure from a schema we were probably expecting it to use the namespace in which it was defined, so we'd need something like <bar:Foo xmlns:bar="http://bar/" bar:hello="world" />
If an annotation contains tokens other than documentation
an unnecessary io.EOF
error can be passed.
Example element:
<xsd:element name="Country_ISO_Code" type="xsd:string" maxOccurs="1">
<xsd:annotation>
<xsd:documentation>ISO Code identifying the country where the naming rules for this name are defined.</xsd:documentation>
<xsd:appinfo>
<wd:Validation>
<xsd:documentation>A valid instance of Country must exist for the value of Country ISO Code.</xsd:documentation>
<wd:Validation_Message>No Country with that Country Code Exists.</wd:Validation_Message>
</wd:Validation>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
Hi,
I just tried the generator and it gives me this error. I'm not sure if it's a bug or I'm missing something.
$ xsdgen -pkg=types -o=types/evtAdmPrelim.go xsd/evtAdmPrelim.xsd
could not find ref ds:Signature in <xs:element ref="ds:Signature" xmlns="http://www.esocial.gov.br/schema/evt/evtAdmPrelim/v02_04_00" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:xs="http://www.w3.org/2001/XMLSchema"></xs:element>
The tags of go struct generated by xsdgen have unnecessary xmlns. This causes unnecessary xmlns while marshalling into xml.
For example, from http://www.fdsn.org/xml/station/fdsn-station-1.0.xsd, one of the generated go struct is like this:
type BaseFilterType struct {
ResourceId string `xml:"resourceId,attr"`
Name string `xml:"name,attr"`
Items []string `xml:",any"`
Description string `xml:"http://www.fdsn.org/xml/station/1 Description"`
...
}
When marshalling, the Description
will be this:
<Description xmlns="http://www.fdsn.org/xml/station/1"></Description>
The xmlns in elements should be omitted. And only add xmlns in the tag with "attr" of root element.
I'm using the package to create structs of some large XSD files. However I keep getting some error which I can't resolve. I'm not sure whether this is a bug or if I'm doing something wrong myself.
gen.go looks like this:
package schemas
//go:generate xsdgen -ns http://www.w3.org/2000/09/xmldsig# -pkg schemas -o xmldsig-core-schema.go xmldsig-core-schema.xsd
//go:generate xsdgen -ns http://www.registryagency.bg/schemas/deedv2/Fields -pkg schemas -o FieldsSchema.go FieldsSchema.xsd
//go:generate xsdgen -ns http://www.registryagency.bg/schemas/deedv2 -pkg schemas -o DeedV2.go DeedV2.xsd FieldsSchema.xsd
//go:generate xsdgen -ns http://www.registryagency.bg/schemas/envelopev2 -pkg schemas -o Envelopev2.go FieldsSchema.xsd DeedV2.xsd Envelopev2.xsd
The first two lines go well, it's DeedV2.xsd which creates the error: 2018/01/14 06:27:39 complexType SubDeedType: could not find type "_anon148" in namespace http://www.registryagency.bg/schemas/deedv2 for element UIC
The XSD schemas are quite big, even the, in the error mentioned, SubDeedType is already a couple of hundred lines, so I put the schemas on pastebin instead of pasting them here:
DeedV2.xsd: https://pastebin.com/CSaKzDXn
FieldsSchema.xsd: https://pastebin.com/MuSrR29n
EnvelopeV2.xsd: https://pastebin.com/ipjwzkKN
xmldsig-core-schema.xsd: https://pastebin.com/VG14UTuJ
Any help is highly appreciated.
I cannot, despite my best efforts, find a way to decode the response body.
The envelope is processed fine, just the contents of the Body is not decoded.
I think we need a test case for the generated code, as #25 calls for
In xsdgen/xsdgen.go, any restrictions on types aren't given special treatment, only a simple comment is added to the resulting type.
This should be changed to proper Mashal/Unmarshal methods so full XML validation is possible.
Error: Server Error
The server encountered a temporary error and could not complete your request.
Please try again in 30 seconds.
Such as the following diff:
diff --git a/xsd/testdata/ComplexType.xsd b/xsd/testdata/ComplexType.xsd
index f7f060b..dcf9ce8 100644
--- a/xsd/testdata/ComplexType.xsd
+++ b/xsd/testdata/ComplexType.xsd
@@ -5,12 +5,13 @@
<element name='ContactTitle' type='string'/>
<element name='Phone' type='string'/>
<element name='Fax' minOccurs='0' type='string'/>
- <element name='FullAddress' type='tns:AddressType'/>
+ <element name='FullAddress' ref='tns:AddressType'/>
</sequence>
<attribute name='CustomerID' type='token'/>
</complexType>
-<complexType name='AddressType'>
+<element name='AddressType'>
+<complexType>
<sequence>
<element name='Address' type='string'/>
<element name='City' type='string'/>
@@ -20,3 +21,4 @@
</sequence>
<attribute name='CustomerID' type='token'/>
</complexType>
+</element>
diff --git a/xsd/xsd_test.go b/xsd/xsd_test.go
index 5307fe3..6c461e5 100644
--- a/xsd/xsd_test.go
+++ b/xsd/xsd_test.go
@@ -151,7 +151,7 @@ func unmarshal(t *testing.T, data []byte) blob {
func parseFragment(t *testing.T, filename string) Schema {
const tmpl = `<schema targetNamespace="tns" ` +
- `xmlns="http://www.w3.org/2001/XMLSchema">%s</schema>`
+ `xmlns="http://www.w3.org/2001/XMLSchema" xmlns:tns="tns">%s</schema>`
data, err := ioutil.ReadFile(filename)
if err != nil {
t.Fatal(err)
When I try to get the wsdlgen package with 'go get github.com/droyo/go-xml/wsdlgen', I get:
../github.com/droyo/go-xml/wsdlgen/cli.go:10:2: use of internal package not allowed
../github.com/droyo/go-xml/wsdlgen/cli.go:11:2: use of internal package not allowed
I understood that referencing internal packages outside the tree is not allowed. Any ideas how I can fix this? Thanks!
It would be nice to make types derived from XSD's duration
type use time.Duration
, and provide Marshal/UnmarshalXML methods for it. It's an... interesting notation, described here:
Currently it appears the XSDGEN tool does not reference and included/imported schema files.
There is an 'Import' function in the Parser code, but unclear how to use it.
Is there any documentation on how to Code-Gen an XSD Schema that uses extensive external imports ?
My use case is an open standard domain specific XML Schema, which has an extensive data model and uses about 20 different included files (With nested includes/imports).
I can share the entire schema if required as it is public.
Consider the element declaration below (note the "maxOccurs" attribute)
<element name="timestamps" type="xsd:dateTime" minOccurs="0" maxOccurs="unbounded">
In Go, the ideal declaration for this type would be []time.Time. For scalar types, we can get away with converting a *time.Time
to an *xsdDateTime
, a private type that we can attach MarshalXML/UnmarshalXML
methods to. This doesn't work with slice types: https://play.golang.org/p/ZAOS7gzcUk .
Currently, xsdgen
doesn't work at all for this scenario; it will generate code that tries to convert a *[]time.Time
to an *xsdDateTime
, which fails to compile.
A WSDL can define multiple related services in a single definition, but wsdlgen will only output the last one to appear in the file.
It might be nice to alter wsdl.Parse to return a slice of Definitions instead of a single one and then have wsdlgen create clients for each service. Or at least a single client with appropriate methods calling the right services for each port.
WSDL allows for the definition of faults that a given RPC may through. They can be defined in the XML schema like any other type.
If we detect that a type is used as a fault, we should generate an Error() string
method so that the type can implement the error
interface, and try to unmarshal these faults when making SOAP API calls.
Hi,
I have an XSD like the following:
<?xml version="1.0" encoding="utf-8" ?>
<xs:schema id="listTest" targetNamespace="http://tempuri.org/listTest.xsd" elementFormDefault="qualified"
xmlns="http://tempuri.org/listTest.xsd"
xmlns:mstns="http://tempuri.org/listTest.xsd"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="outerList">
<xs:complexType>
<xs:sequence>
<xs:element name="interList1" minOccurs="1" maxOccurs="1">
<xs:complexType>
<xs:sequence>
<xs:element name="innerItem" type="xs:string" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="innerComplex" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="innerComplexStr1" type="xs:string" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="innerComplexStr2" type="xs:string" minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="interList2" minOccurs="1" maxOccurs="1">
<xs:complexType>
<xs:sequence>
<xs:element name="innerItem" type="xs:string" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="innerComplex" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="innerComplexInt1" type="xs:int" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="innerComplexInt2" type="xs:int" minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
As you can see, there's two complex elements that don't occur at the root level and both share the name innerComplex
. The second shadows the first when I run xsdgen
and this is the generated Go file:
package ws
type InnerComplex struct {
InnerComplexStr1 []string `xml:"http://tempuri.org/listTest.xsd innerComplexStr1,omitempty"`
InnerComplexStr2 []string `xml:"http://tempuri.org/listTest.xsd innerComplexStr2,omitempty"`
}
type InterList1 struct {
InnerItem []string `xml:"http://tempuri.org/listTest.xsd innerItem,omitempty"`
InnerComplex []InnerComplex `xml:"http://tempuri.org/listTest.xsd innerComplex,omitempty"`
}
type InterList2 struct {
InnerItem []string `xml:"http://tempuri.org/listTest.xsd innerItem,omitempty"`
InnerComplex []InnerComplex `xml:"http://tempuri.org/listTest.xsd innerComplex,omitempty"`
}
type OuterList struct {
InterList1 InterList1 `xml:"http://tempuri.org/listTest.xsd interList1"`
InterList2 InterList2 `xml:"http://tempuri.org/listTest.xsd interList2"`
}
If I rename the second occurrence of innerComplex
to something else
<?xml version="1.0" encoding="utf-8" ?>
<xs:schema id="listTest" targetNamespace="http://tempuri.org/listTest.xsd" elementFormDefault="qualified"
xmlns="http://tempuri.org/listTest.xsd"
xmlns:mstns="http://tempuri.org/listTest.xsd"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="outerList">
<xs:complexType>
<xs:sequence>
<xs:element name="interList1" minOccurs="1" maxOccurs="1">
<xs:complexType>
<xs:sequence>
<xs:element name="innerItem" type="xs:string" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="innerComplex" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="innerComplexStr1" type="xs:string" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="innerComplexStr2" type="xs:string" minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="interList2" minOccurs="1" maxOccurs="1">
<xs:complexType>
<xs:sequence>
<xs:element name="innerItem" type="xs:string" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="innerComplexADifferentName" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="innerComplexInt1" type="xs:int" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="innerComplexInt2" type="xs:int" minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
The generated Go code can parse the XML file represented by the schema correctly.
package ws
type InnerComplex struct {
InnerComplexStr1 []string `xml:"http://tempuri.org/listTest.xsd innerComplexStr1,omitempty"`
InnerComplexStr2 []string `xml:"http://tempuri.org/listTest.xsd innerComplexStr2,omitempty"`
}
type InnerComplexADifferentName struct {
InnerComplexInt1 []int `xml:"http://tempuri.org/listTest.xsd innerComplexInt1,omitempty"`
InnerComplexInt2 []int `xml:"http://tempuri.org/listTest.xsd innerComplexInt2,omitempty"`
}
type InterList1 struct {
InnerItem []string `xml:"http://tempuri.org/listTest.xsd innerItem,omitempty"`
InnerComplex []InnerComplex `xml:"http://tempuri.org/listTest.xsd innerComplex,omitempty"`
}
type InterList2 struct {
InnerItem []string `xml:"http://tempuri.org/listTest.xsd innerItem,omitempty"`
InnerComplexADifferentName []InnerComplexADifferentName `xml:"http://tempuri.org/listTest.xsd innerComplexADifferentName,omitempty"`
}
type OuterList struct {
InterList1 InterList1 `xml:"http://tempuri.org/listTest.xsd interList1"`
InterList2 InterList2 `xml:"http://tempuri.org/listTest.xsd interList2"`
}
I'm terribly sorry about the very artificial looking examples. I hacked it up looking at one of your test-cases. This is the first time I'm having to parse some bulky legacy XML (270kb schema file) and even what code's been generated so far has been greatly helpful!
I'm trying to generate go code from Amazon MWS xsd schema.
Below are files to reproduce the issue:
$ cat ./generate-amazon-mws-from-xsd
#!/bin/bash
wget -N -P zzz -i ./amazon-mws-xsd-urls -B "https://images-na.ssl-images-
amazon.com/images/G/01/rainier/help/xsd/release_4_1/"
cd xxx
go generate
$ cat ./amazon-mws-xsd-urls
amzn-envelope.xsd
amzn-header.xsd
amzn-base.xsd
Inventory.xsd
Price.xsd
OrderAcknowledgement.xsd
OrderFulfillment.xsd
$ cat ./xxx/xxx.go
package main
//go:generate xsdgen ../zzz/amzn-base.xsd ../zzz/Price.xsd
Namely, first file is a bash script to download schema files, second file is a list of required files and third is a go file with go generate
directive.
First of all, I had to change aqwari.net/xml/xsd/parse.go#parseInt()
as following:
func parseInt(s string) int {
s = strings.TrimSpace(s)
switch s {
case "":
return 0
case "unbounded":
return -1
}
n, err := strconv.Atoi(s)
if err != nil {
//TNL: attempt to parse from float
f, err := strconv.ParseFloat(s, 64)
if err != nil {
stop(err.Error())
}
n = int(f)
}
return n
}
otherwise it failed to parse <xsd:minInclusive value=".01"/>
on restriction on xsd:decimal
type.
This is an issue by itself, but I want just to fast generate go structs and edit them manually, so I don't bother with validations for the moment.
After that, go generate
finished without error, but created an empty output file with only package
directive in it.
I also tried to put schema files into the same dir as go file, path is under $GOPATH/src.
Could you please suggest what am I doing wrong or suggest a quick workaround till it fixed?
I need the example for AllowType, Only Types, ProcessTypes etc. Please share the example program
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.