forked from droyo/go-xml
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathxsd.go
331 lines (306 loc) · 10.6 KB
/
xsd.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
// Package xsd parses type declarations in XML Schema documents.
//
// The xsd package implements a parser for a subset of the XML Schema
// standard. This package is intended for use in code-generation programs for
// client libraries, and as such, does not validate XML Schema documents,
// nor does it provide sufficient information to validate the documents
// described by a schema. Notably, the xsd package does not preserve
// information about element or attribute groups. Instead, all groups
// are de-referenced before parsing is done, and all nested sequences of
// elements are flattened.
//
// The xsd package respects XML name spaces in schema documents, and can
// parse schema documents that import or include other schema documents.
package xsd // import "aqwari.net/xml/xsd"
import (
"bytes"
"encoding/xml"
"fmt"
"io"
"regexp"
"time"
"aqwari.net/xml/xmltree"
)
const (
schemaNS = "http://www.w3.org/2001/XMLSchema"
schemaInstanceNS = "http://www.w3.org/2001/XMLSchema-instance"
)
// Types in XML Schema Documents are derived from one of the built-in types
// defined by the standard, by restricting or extending the range of values
// a type may contain. A Type may be one of *SimpleType, *ComplexType,
// or Builtin.
type Type interface {
// just for compile-time type checking
isType()
}
// An Element describes an XML element that may appear as part of a complex
// type. Elements may have restrictions about the number of times they may
// appear and they values they may contain. The xsd package converts this
// low-level information into boolean flags where appropriate.
//
// http://www.w3.org/TR/2004/REC-xmlschema-1-20041028/structures.html#element-element
type Element struct {
// Annotations for this element
Doc string
// The canonical name of this element
Name xml.Name
// 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
// Type of this element.
Type Type
// An abstract type does not appear in the xml document, but
// is "implemented" by other types in its substitution group.
Abstract bool
// True if maxOccurs > 1 or maxOccurs == "unbounded"
Plural bool
// True if the element is optional.
Optional bool
// If true, this element will be declared as a pointer.
Nillable bool
// Default overrides the zero value of this element.
Default string
// Any additional attributes provided in the <xs:element> element.
Attr []xml.Attr
// Used for resolving prefixed strings in extra attribute values.
xmltree.Scope
}
// An Attribute describes the key=value pairs that may appear within the
// opening tag of an element. Only complex types may contain attributes.
// the Type of an Attribute can only be a Builtin or SimpleType.
//
// http://www.w3.org/TR/2004/REC-xmlschema-1-20041028/structures.html#element-attribute
type Attribute struct {
// The canonical name of this attribute. It is uncommon for attributes
// to have a name space.
Name xml.Name
// 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.
Type Type
// True if this attribute has a <list> simpleType
Plural bool
// Default overrides the zero value of this element.
Default string
// True if the attribute is not required
Optional bool
// Any additional attributes provided in the <xs:attribute> element.
Attr []xml.Attr
// Used for resolving qnames in additional attributes.
xmltree.Scope
}
// A Schema is the decoded form of an XSD <schema> element. It contains
// a collection of all types declared in the schema. Top-level elements
// are not recorded in a Schema.
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"`
// Types defined in this schema declaration
Types map[xml.Name]Type
// Any annotations declared at the top-level of the schema, separated
// by new lines.
Doc string
}
// FindType looks for a type by its canonical name. In addition to the types
// declared in a Schema, FindType will also search through the types that
// a Schema's top-level types are derived from. FindType will return nil if
// a type could not be found with the given name.
func (s *Schema) FindType(name xml.Name) Type {
for _, t := range s.Types {
if t := findType(t, name); t != nil {
return t
}
}
return nil
}
func findType(t Type, name xml.Name) Type {
if XMLName(t) == name {
return t
}
if b := Base(t); b != nil {
return findType(b, name)
}
return nil
}
// An XSD type can reference other types when deriving new types or
// describing elements. These types don't have to appear in-order; a type
// may be declared before its dependencies. To handle this, we define a
// "stub" Type, which we can resolve in a second pass.
type linkedType xml.Name
func (linkedType) isType() {}
// A ComplexType describes an XML element that may contain attributes
// and elements in its content. Complex types are derived by extending
// or restricting another type. The xsd package records the elements and
// attributes that may occur in an xml element conforming to the type.
// A ComplexType is part of a linked list, through its Base field, that is
// guaranteed to end in the Builtin value AnyType.
//
// http://www.w3.org/TR/2004/REC-xmlschema-1-20041028/structures.html#element-complexType
type ComplexType struct {
// Annotations provided by the schema author.
Doc string
// The canonical name of this type.
Name xml.Name
// The type this type is derived from.
Base Type
// True if this is an anonymous type
Anonymous bool
// XML elements that this type may contain in its content.
Elements []Element
// Possible attributes for the element's opening tag.
Attributes []Attribute
// An abstract type does not appear in the xml document, but
// is "implemented" by other types in its substitution group.
Abstract bool
// If true, this type is an extension to Base. Otherwise,
// this type is derived by restricting the set of elements and
// attributes allowed in Base.
Extends bool
// If true, this type is allowed to contain character data that is
// not part of any sub-element.
Mixed bool
}
func (*ComplexType) isType() {}
// A SimpleType describes an XML element that does not contain elements
// or attributes. SimpleTypes are suitable for use as attribute values.
// A SimpleType can be an "atomic" type (int, string, etc), or a list of
// atomic types, separated by white space. In addition, a SimpleType may
// be declared as a union; or one of a set of SimpleTypes. A SimpleType
// is guaranteed to be part of a linked list, through its Base field,
// that ends in a Builtin value.
//
// http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/datatypes.html#element-simpleType
//
// http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/datatypes.html#element-union
//
// http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/datatypes.html#element-list
type SimpleType struct {
// True if this is an anonymous type
Anonymous bool
// True if this type is a whitespace-delimited list, with
// items of type Base.
List bool
// A simpleType may be described as a union: one of many
// possible simpleTypes.
Union []Type
// Restrictions on this type's values
Restriction Restriction
// The canonical name of this type
Name xml.Name
// Any annotations for this type, as provided by the schema
// author.
Doc string
// The type this type is derived from. This is guaranteed to be
// part of a linked list that always ends in a Builtin type.
Base Type
}
func (*SimpleType) isType() {}
// A SimpleType can be derived from a built-in or SimpleType by
// restricting the set of values it may contain. The xsd package only
// records restrictions that are useful for generating client libraries,
// and not for validating documents.
//
// http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/datatypes.html#element-restriction
type Restriction struct {
// The max digits to the right of the decimal point for
// floating-point values.
Precision int
// If len(Enum) > 0, the type must be one of the values contained
// in Enum.
Enum []string
// The minimum and maximum (exclusive) value of this type, if
// numeric
Min, Max float64
// Exact, maximum and minimum length (in characters) of this type
Length, MinLength, MaxLength int
MinDate, MaxDate time.Time
// Regular expression that values of this type must match
Pattern *regexp.Regexp
// The exact number of digits allowed
TotalDigits int
// Any annotations for the restriction, if present.
Doc string
}
type annotation string
func (a annotation) append(extra annotation) annotation {
if a != "" {
a += "\n\n"
}
return a + extra
}
// An <xs:annotation> element may contain zero or more <xs:documentation>
// children. The xsd package joins the content of these children, separated
// with blank lines.
func (doc *annotation) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
buf := make([][]byte, 1)
var (
tok xml.Token
err error
)
Loop:
for {
tok, err = d.Token()
if err != nil {
break
}
switch tok := tok.(type) {
case xml.EndElement:
break Loop
case xml.StartElement:
if (tok.Name != xml.Name{schemaNS, "documentation"}) {
if err := d.Skip(); err != nil {
return err
}
}
var frag []byte
if err := d.DecodeElement(&frag, &tok); err != nil {
return err
}
buf = append(buf, bytes.TrimSpace(frag))
}
}
*doc = annotation(bytes.TrimSpace(bytes.Join(buf, []byte("\n\n"))))
if err == io.EOF {
return nil
}
return err
}
// XMLName returns the canonical xml name of a Type.
func XMLName(t Type) xml.Name {
switch t := t.(type) {
case *SimpleType:
return t.Name
case *ComplexType:
return t.Name
case Builtin:
return t.Name()
case linkedType:
return xml.Name(t)
}
panic(fmt.Sprintf("xsd: unexpected xsd.Type %[1]T %[1]v passed to XMLName", t))
}
// Base returns the base type that a Type is derived from.
// If the value is of type Builtin, Base will return nil.
func Base(t Type) Type {
switch t := t.(type) {
case *ComplexType:
return t.Base
case *SimpleType:
return t.Base
case Builtin:
return nil
case linkedType:
return nil
}
panic(fmt.Sprintf("xsd: unexpected xsd.Type %[1]T %[1]v passed to Base", t))
}
// The xsd package bundles a number of well-known schemas.
// These schemas are always added to the list of available schema
// when parsing an XML schema using the Parse function.
var StandardSchema = [][]byte{
soapenc11xsd, // http://schemas.xmlsoap.org/soap/encoding/
xmlnsxsd, // http://www.w3.org/XML/1998/namespace
wsdl2003xsd, // http://schemas.xmlsoap.org/wsdl/
xlinkxsd, // http://www.w3.org/1999/xlink
}