xml-toolkit 1.0.61 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -45
- package/index.js +12 -6
- package/lib/XMLMarshaller.js +1 -1
- package/lib/XMLParser.js +42 -13
- package/lib/XMLReader.js +44 -0
- package/lib/XMLSchema.js +11 -0
- package/lib/XMLSchemaBuiltIn.js +20 -5
- package/lib/XMLSchemaXml.js +0 -1
- package/lib/XMLSchemata.js +94 -11
- package/lib/XSAnyType.js +98 -0
- package/lib/simple/DT7.js +248 -0
- package/lib/simple/XSSimpleType.js +259 -0
- package/lib/simple/XSSimpleTypeBoolean.js +91 -0
- package/lib/simple/XSSimpleTypeDT.js +96 -0
- package/lib/simple/XSSimpleTypeDecimal.js +130 -0
- package/lib/simple/XSSimpleTypeFloatingPoint.js +80 -0
- package/lib/simple/XSSimpleTypeInteger.js +72 -0
- package/lib/simple/XSSimpleTypeQName.js +24 -0
- package/lib/validation/Match.js +54 -0
- package/lib/validation/MatchAll.js +33 -0
- package/lib/validation/MatchAny.js +15 -0
- package/lib/validation/MatchChoice.js +74 -0
- package/lib/validation/MatchElement.js +45 -0
- package/lib/validation/MatchSequence.js +84 -0
- package/lib/validation/Occurable.js +88 -0
- package/lib/validation/XMLValidationState.js +175 -0
- package/lib/validation/XMLValidatior.js +38 -0
- package/lib/xml.xsd +1 -1
- package/package.json +1 -1
- package/lib/XSSimpleType.js +0 -329
package/README.md
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|

|
|
2
2
|

|
|
3
|
+
[](https://deepwiki.com/do-/node-xml-toolkit)
|
|
3
4
|
|
|
4
5
|
`node-xml-toolkit` is a pure node.js library for solving diverse XML related application tasks, e. g.:
|
|
5
6
|
* scanning through multi gigabyte long XML files with a limited memory buffer;
|
|
@@ -13,16 +14,19 @@ It features both (fast and simple, but greedy) [synchronous](https://github.com/
|
|
|
13
14
|
npm install xml-toolkit
|
|
14
15
|
```
|
|
15
16
|
|
|
16
|
-
#
|
|
17
|
-
|
|
18
|
-
* [Parsing a small file completely](XMLParser)
|
|
17
|
+
# Usage
|
|
18
|
+
* [Parsing a small file completely](XMLParser) (optionally validating)
|
|
19
19
|
|
|
20
20
|
```js
|
|
21
21
|
const fs = require ('fs')
|
|
22
|
-
const {XMLParser} = require ('xml-toolkit')
|
|
22
|
+
const {XMLParser, XMLSchemata} = require ('xml-toolkit')
|
|
23
23
|
|
|
24
24
|
const xml = fs.readFileSync ('doc.xml')
|
|
25
|
-
const
|
|
25
|
+
// const xs = new XMLSchemata ('schema.xsd')
|
|
26
|
+
|
|
27
|
+
const parser = new XMLParser ({
|
|
28
|
+
// xs,
|
|
29
|
+
})
|
|
26
30
|
|
|
27
31
|
const document = parser.process (xml)
|
|
28
32
|
|
|
@@ -31,12 +35,15 @@ for (const element of document.detach ().children) {
|
|
|
31
35
|
}
|
|
32
36
|
```
|
|
33
37
|
|
|
34
|
-
* [Reading a Record List](https://github.com/do-/node-xml-toolkit/wiki/Use-Case:-Reading-a-Record-List)
|
|
38
|
+
* [Reading a Record List](https://github.com/do-/node-xml-toolkit/wiki/Use-Case:-Reading-a-Record-List) (optionally validating)
|
|
35
39
|
|
|
36
40
|
```js
|
|
37
|
-
const {XMLReader, XMLNode} = require ('xml-toolkit')
|
|
41
|
+
const {XMLReader, XMLNode, XMLSchemata} = require ('xml-toolkit')
|
|
42
|
+
|
|
43
|
+
// const xs = new XMLSchemata ('schema.xsd')
|
|
38
44
|
|
|
39
45
|
const records = new XMLReader ({
|
|
46
|
+
//xs,
|
|
40
47
|
filterElements : 'Record',
|
|
41
48
|
map : XMLNode.toObject ({})
|
|
42
49
|
}).process (xmlSource)
|
|
@@ -56,7 +63,7 @@ const records = new XMLReader ({
|
|
|
56
63
|
// records.on ('data', record => doSomethingWith (record))
|
|
57
64
|
```
|
|
58
65
|
|
|
59
|
-
* [Getting a Single Element](https://github.com/do-/node-xml-toolkit/wiki/Use-Case:-Getting-a-Single-Element)
|
|
66
|
+
* [Getting a Single Element](https://github.com/do-/node-xml-toolkit/wiki/Use-Case:-Getting-a-Single-Element)
|
|
60
67
|
|
|
61
68
|
```js
|
|
62
69
|
const {XMLReader, XMLNode} = require ('xml-toolkit')
|
|
@@ -67,6 +74,21 @@ const data = await new XMLReader ({
|
|
|
67
74
|
}).process (xmlSource).findFirst ()
|
|
68
75
|
```
|
|
69
76
|
|
|
77
|
+
* [Formatting XML](https://github.com/do-/node-xml-toolkit/wiki/XMLPrinter)
|
|
78
|
+
```js
|
|
79
|
+
const {XMLParser} = require ('xml-toolkit')
|
|
80
|
+
xml = new XMLParser ()
|
|
81
|
+
.process (fs.readFileSync ('doc.xml'))
|
|
82
|
+
.toString ({
|
|
83
|
+
// decl: {encoding: 'UTF-8', standalone: 1},
|
|
84
|
+
space: '\t',
|
|
85
|
+
// attrSpace: 2,
|
|
86
|
+
// EOL: '\n',
|
|
87
|
+
// level: 0,
|
|
88
|
+
// encodeLineBreaks: false,
|
|
89
|
+
})
|
|
90
|
+
```
|
|
91
|
+
|
|
70
92
|
* [Patching XML](https://github.com/do-/node-xml-toolkit/wiki/Use-Case:-Patching-XML)
|
|
71
93
|
|
|
72
94
|
```js
|
|
@@ -80,8 +102,6 @@ let xmlResult = ''; for await (const node of new XMLReader ().process (xmlSource
|
|
|
80
102
|
* [Serializing an Object According to an XML Schema](https://github.com/do-/node-xml-toolkit/wiki/Use-Case:-Serializing-an-Object-According-to-an-XML-Schema)
|
|
81
103
|
|
|
82
104
|
```js
|
|
83
|
-
const {XMLSchemata} = require ('xml-toolkit')
|
|
84
|
-
|
|
85
105
|
const data = {ExportDebtRequestsResponse: {
|
|
86
106
|
"request-data": {
|
|
87
107
|
// ...
|
|
@@ -98,14 +118,13 @@ const xml = xs.stringify (data)
|
|
|
98
118
|
<!-- ... and so on ... -->
|
|
99
119
|
*/
|
|
100
120
|
```
|
|
101
|
-
|
|
102
|
-
* Invoking a [SOAP 1.1](https://github.com/do-/node-xml-toolkit/wiki/SOAP11) or [SOAP 1.2](https://github.com/do-/node-xml-toolkit/wiki/SOAP12) Web Service
|
|
121
|
+
* Invoking a [SOAP 1.1](SOAP11) Web Service
|
|
103
122
|
|
|
104
123
|
```js
|
|
105
124
|
const http = require ('http')
|
|
106
|
-
const {SOAP11
|
|
125
|
+
const {SOAP11} = require ('xml-toolkit')
|
|
107
126
|
|
|
108
|
-
const soap = new SOAP11 ('their.wsdl')
|
|
127
|
+
const soap = new SOAP11 ('their.wsdl')
|
|
109
128
|
|
|
110
129
|
const {method, headers, body} = soap.http ({RequestElementNameOfTheirs: {amount: '0.01'}})
|
|
111
130
|
|
|
@@ -113,32 +132,6 @@ const rq = http.request (endpointURL, {method, headers})
|
|
|
113
132
|
rq.write (body)
|
|
114
133
|
```
|
|
115
134
|
|
|
116
|
-
* [Implementing](https://github.com/do-/node-xml-toolkit/wiki/Use-Case:-Implement-a-SOAP-Web-Service) a SOAP service
|
|
117
|
-
|
|
118
|
-
```js
|
|
119
|
-
const {XMLSchemata, SOAP11, SOAP12, SOAPFault} = require ('xml-toolkit')
|
|
120
|
-
|
|
121
|
-
const SOAP = SOAP11 // or SOAP12
|
|
122
|
-
|
|
123
|
-
const xs = new XMLSchemata (`myService.wsdl`)
|
|
124
|
-
|
|
125
|
-
let body, statusCode; try {
|
|
126
|
-
body = xs.stringify (myMethod (/*...*/))
|
|
127
|
-
statusCode = 200
|
|
128
|
-
}
|
|
129
|
-
catch (x) {
|
|
130
|
-
body = new SOAPFault (x)
|
|
131
|
-
statusCode = 500
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
rp.writeHead (statusCode, {
|
|
135
|
-
'Content-Type': SOAP.contentType,
|
|
136
|
-
})
|
|
137
|
-
|
|
138
|
-
const xml = SOAP.message (body)
|
|
139
|
-
rp.end (xml)
|
|
140
|
-
```
|
|
141
|
-
|
|
142
135
|
# Motivation
|
|
143
136
|
|
|
144
137
|
Unlike Java (with [JAXB](https://www.oracle.com/technical-resources/articles/javase/jaxb.html) and [JAX-WS](https://www.oracle.com/technical-resources/articles/javase/jax-ws-2.html)) and some other software development platforms dating back to late 1990s, the core node.js library doesn't offer any standard tool for dealing with XML.
|
|
@@ -154,9 +147,6 @@ Pure js 3rd party modules are abundant, but after some real tasks based research
|
|
|
154
147
|
|
|
155
148
|
No W3C specification is 100% implemented here. For instance, DTDs are not supported, so, in theory, any rogue XML file using such bizarre deprecated feature as [Entity Declarations](https://www.w3.org/TR/xml/#sec-entity-decl) may crash the local XML parser.
|
|
156
149
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
In short, `node-xml-toolkit` may produce incorrect results for some input data, especially for deliberately broken ones.
|
|
150
|
+
The [XMLSchema](https://github.com/do-/node-xml-toolkit/wiki/XMLSchemata) is fairly usable for most real world cases, still some features (like `xs:unique`) are ignored completely, some others may require more test coverage. In general, XML Schema support in `xml-toolkit` should be considered _forever beta_.
|
|
160
151
|
|
|
161
|
-
There are perfectly reliable external tools for XML validation: for instance,
|
|
162
|
-
[xmllint](https://linux.die.net/man/1/xmllint) (used in the test suite here) do just fine.
|
|
152
|
+
There are perfectly reliable external tools for XML validation: for instance, [xmllint](https://linux.die.net/man/1/xmllint) (used in the test suite here) do just fine.
|
package/index.js
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
const {
|
|
2
2
|
XSSimpleType,
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
} = require ('./lib/simple/XSSimpleType')
|
|
4
|
+
|
|
5
|
+
const {
|
|
5
6
|
XSSimpleTypeDate,
|
|
6
7
|
XSSimpleTypeDateTime,
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
} = require ('./lib/simple/XSSimpleTypeDT')
|
|
9
|
+
|
|
10
|
+
const {
|
|
11
|
+
XSSimpleTypeFloat,
|
|
12
|
+
XSSimpleTypeDouble,
|
|
13
|
+
} = require ('./lib/simple/XSSimpleTypeFloatingPoint')
|
|
9
14
|
|
|
10
15
|
module.exports = {
|
|
11
16
|
AttributesMap: require ('./lib/AttributesMap'),
|
|
@@ -28,10 +33,11 @@ module.exports = {
|
|
|
28
33
|
XMLSchemata: require ('./lib/XMLSchemata'),
|
|
29
34
|
XSSimpleType,
|
|
30
35
|
XSSimpleTypeFloat,
|
|
31
|
-
|
|
36
|
+
XSSimpleTypeDouble,
|
|
37
|
+
XSSimpleTypeBoolean: require ('./lib/simple/XSSimpleTypeBoolean'),
|
|
32
38
|
XSSimpleTypeDate,
|
|
33
39
|
XSSimpleTypeDateTime,
|
|
34
|
-
XSSimpleTypeQName,
|
|
40
|
+
XSSimpleTypeQName: require ('./lib/simple/XSSimpleTypeQName'),
|
|
35
41
|
}
|
|
36
42
|
|
|
37
43
|
module.exports.SOAP = v => {switch (String (v)) {
|
package/lib/XMLMarshaller.js
CHANGED
|
@@ -60,7 +60,7 @@ const XMLMarshaller = class {
|
|
|
60
60
|
|
|
61
61
|
const {targetNamespace, attributes: {name}} = schemaElement
|
|
62
62
|
|
|
63
|
-
throw Error (`Cannot stringify ${JSON.stringify (data)} as ${name}#{${targetNamespace}}`, {cause})
|
|
63
|
+
throw Error (`Cannot stringify ${JSON.stringify (data)} as ${name}#{${targetNamespace}}: ${cause.message}`, {cause})
|
|
64
64
|
|
|
65
65
|
}
|
|
66
66
|
|
package/lib/XMLParser.js
CHANGED
|
@@ -1,21 +1,40 @@
|
|
|
1
|
-
const
|
|
2
|
-
const
|
|
3
|
-
const
|
|
4
|
-
const
|
|
1
|
+
const SAXEvent = require ('./SAXEvent.js')
|
|
2
|
+
const XMLNode = require ('./XMLNode.js')
|
|
3
|
+
const XMLIterator = require ('./XMLIterator.js')
|
|
4
|
+
const XMLValidatior = require ('./validation/XMLValidatior.js')
|
|
5
5
|
|
|
6
6
|
const XMLParser = class {
|
|
7
7
|
|
|
8
8
|
constructor (options = {}) {
|
|
9
|
-
|
|
10
|
-
if (!('stripSpace' in options)) options.stripSpace = true
|
|
11
|
-
assert (options.stripSpace === true || options.stripSpace === false, 'options.stripSpace must be boolean, not ' + typeof options.stripSpace)
|
|
12
9
|
|
|
13
|
-
if (
|
|
14
|
-
|
|
10
|
+
if ('xs' in options) {
|
|
11
|
+
|
|
12
|
+
const {xs} = options; if (!(xs instanceof Map && xs.constructor.name === 'XMLSchemata')) throw Error (`options.xs must be an XMLSchemata instance, found ${typeof xs} '${xs}'`)
|
|
13
|
+
|
|
14
|
+
this.xs = xs
|
|
15
|
+
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
|
|
19
|
+
this.xs = null
|
|
20
|
+
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
for (const k of ['stripSpace', 'useEntities', 'useNamespaces']) {
|
|
24
|
+
|
|
25
|
+
if (!(k in options)) options [k] = true
|
|
15
26
|
|
|
16
|
-
|
|
17
|
-
|
|
27
|
+
switch (options [k]) {
|
|
28
|
+
case false:
|
|
29
|
+
if (this.xs !== null && k === 'useNamespaces') throw Error (`With an XMLSchema provided, options.${k} cannot be false`)
|
|
30
|
+
case true:
|
|
31
|
+
break
|
|
32
|
+
default:
|
|
33
|
+
throw Error (`options.${k} must be boolean, found ${typeof options [k]} '${options [k]}'`)
|
|
34
|
+
}
|
|
18
35
|
|
|
36
|
+
}
|
|
37
|
+
|
|
19
38
|
this.stripSpace = options.stripSpace
|
|
20
39
|
this.useEntities = options.useEntities
|
|
21
40
|
this.useNamespaces = options.useNamespaces
|
|
@@ -46,9 +65,11 @@ const XMLParser = class {
|
|
|
46
65
|
|
|
47
66
|
default:
|
|
48
67
|
|
|
49
|
-
if (this.text.length === 0) break
|
|
50
68
|
if (this.stripSpace) this.text = this.text.trim ()
|
|
51
69
|
if (this.text.length === 0) break
|
|
70
|
+
if (this.validator) {
|
|
71
|
+
this.validator.characters (this.text)
|
|
72
|
+
}
|
|
52
73
|
(new XMLNode (this.text, null, SAXEvent.TYPES.CHARACTERS)).parent = this.element
|
|
53
74
|
this.text = ''
|
|
54
75
|
|
|
@@ -61,14 +82,22 @@ const XMLParser = class {
|
|
|
61
82
|
node.parent = this.element
|
|
62
83
|
if (this.useNamespaces) node.readNamespaces ()
|
|
63
84
|
this.element = node
|
|
64
|
-
if (this.document === null)
|
|
85
|
+
if (this.document === null) {
|
|
86
|
+
this.document = node
|
|
87
|
+
if (this.xs !== null) this.validator = new XMLValidatior (this.xs, node, () => BigInt (nodes.pos))
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
if (this.xs !== null) this.validator.startElement (node)
|
|
91
|
+
}
|
|
65
92
|
break
|
|
66
93
|
|
|
67
94
|
case SAXEvent.TYPES.END_ELEMENT:
|
|
68
95
|
|
|
69
96
|
if (this.element === null) throw new Error (`Unbalanced end element tag "${node.text}" occured at position ${nodes.pos}`)
|
|
97
|
+
node ['_ns_map'] = this.element ['_ns_map']
|
|
70
98
|
this.element.type = type
|
|
71
99
|
this.element = this.element.parent
|
|
100
|
+
if (this.xs !== null) this.validator.endElement (node)
|
|
72
101
|
break
|
|
73
102
|
|
|
74
103
|
}
|
package/lib/XMLReader.js
CHANGED
|
@@ -3,6 +3,7 @@ const {Transform} = require ('stream')
|
|
|
3
3
|
const SAXEvent = require ('./SAXEvent.js')
|
|
4
4
|
const XMLNode = require ('./XMLNode.js')
|
|
5
5
|
const XMLLexer = require ('./XMLLexer.js')
|
|
6
|
+
const XMLValidatior = require ('./validation/XMLValidatior.js')
|
|
6
7
|
|
|
7
8
|
const OPT_SRC = Symbol ('_src')
|
|
8
9
|
const OPT_SAX = Symbol ('_sax')
|
|
@@ -54,6 +55,14 @@ const XMLReader = class extends Transform {
|
|
|
54
55
|
|
|
55
56
|
super (options)
|
|
56
57
|
|
|
58
|
+
if ('xs' in options) {
|
|
59
|
+
this.xs = options.xs
|
|
60
|
+
this.validator = null
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
this.xs = null
|
|
64
|
+
}
|
|
65
|
+
|
|
57
66
|
this.stripSpace = options.stripSpace
|
|
58
67
|
this.useEntities = options.useEntities
|
|
59
68
|
this.useNamespaces = options.useNamespaces
|
|
@@ -133,11 +142,46 @@ const XMLReader = class extends Transform {
|
|
|
133
142
|
return this [OPT_SAX] = false
|
|
134
143
|
|
|
135
144
|
}
|
|
145
|
+
|
|
146
|
+
validate (xmlNode) {
|
|
147
|
+
|
|
148
|
+
if (!this.validator) switch (xmlNode.type) {
|
|
149
|
+
|
|
150
|
+
case SAXEvent.TYPES.START_ELEMENT:
|
|
151
|
+
this.validator = new XMLValidatior (this.xs, xmlNode, () => this.position)
|
|
152
|
+
|
|
153
|
+
default:
|
|
154
|
+
return
|
|
155
|
+
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
switch (xmlNode.type) {
|
|
159
|
+
|
|
160
|
+
case SAXEvent.TYPES.START_ELEMENT : return this.validator.startElement (xmlNode)
|
|
161
|
+
|
|
162
|
+
case SAXEvent.TYPES.CHARACTERS : return this.validator.characters (xmlNode.src)
|
|
163
|
+
|
|
164
|
+
case SAXEvent.TYPES.END_ELEMENT : return this.validator.endElement (xmlNode)
|
|
165
|
+
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
}
|
|
136
169
|
|
|
137
170
|
publish (xmlNode, type = null) {
|
|
138
171
|
|
|
139
172
|
if (type !== null) xmlNode.type = type
|
|
140
173
|
|
|
174
|
+
if (this.xs) try {
|
|
175
|
+
|
|
176
|
+
this.validate (xmlNode)
|
|
177
|
+
|
|
178
|
+
}
|
|
179
|
+
catch (error) {
|
|
180
|
+
|
|
181
|
+
this.destroy (error)
|
|
182
|
+
|
|
183
|
+
}
|
|
184
|
+
|
|
141
185
|
const {filter} = this; if (filter !== null) {
|
|
142
186
|
|
|
143
187
|
if (!filter (xmlNode)) return
|
package/lib/XMLSchema.js
CHANGED
|
@@ -70,6 +70,16 @@ const XMLSchema = class extends Map {
|
|
|
70
70
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
getElement (localName) {
|
|
74
|
+
|
|
75
|
+
const el = this.get (localName)
|
|
76
|
+
|
|
77
|
+
if (!el || el.localName !== 'element') throw Error (`The element ${localName} is not found in ${this.targetNamespace}`)
|
|
78
|
+
|
|
79
|
+
return el
|
|
80
|
+
|
|
81
|
+
}
|
|
82
|
+
|
|
73
83
|
setSource (node) {
|
|
74
84
|
|
|
75
85
|
this.defaultNamespace = node.namespacesMap.default
|
|
@@ -94,6 +104,7 @@ const XMLSchema = class extends Map {
|
|
|
94
104
|
this [TYPES].set (name, e)
|
|
95
105
|
break
|
|
96
106
|
default:
|
|
107
|
+
// console.log ([name, localName, this.targetNamespace])
|
|
97
108
|
this.set (name, e)
|
|
98
109
|
}
|
|
99
110
|
|
package/lib/XMLSchemaBuiltIn.js
CHANGED
|
@@ -2,7 +2,13 @@ const fs = require ('fs')
|
|
|
2
2
|
const path = require ('path')
|
|
3
3
|
|
|
4
4
|
const XMLSchema = require ('./XMLSchema.js')
|
|
5
|
-
const {XSSimpleType} = require ('./XSSimpleType.js')
|
|
5
|
+
const {XSSimpleType} = require ('./simple/XSSimpleType.js')
|
|
6
|
+
const XSSimpleTypeDT = require ('./simple/XSSimpleTypeDT.js')
|
|
7
|
+
const XSSimpleTypeDecimal = require ('./simple/XSSimpleTypeDecimal.js')
|
|
8
|
+
const XSSimpleTypeInteger = require ('./simple/XSSimpleTypeInteger.js')
|
|
9
|
+
const XSSimpleTypeBoolean = require ('./simple/XSSimpleTypeBoolean.js')
|
|
10
|
+
const XSSimpleTypeFloatingPoint = require ('./simple/XSSimpleTypeFloatingPoint.js')
|
|
11
|
+
const XSSimpleTypeQName = require ('./simple/XSSimpleTypeQName.js')
|
|
6
12
|
|
|
7
13
|
class XMLSchemaBuiltIn extends XMLSchema {
|
|
8
14
|
|
|
@@ -14,15 +20,24 @@ class XMLSchemaBuiltIn extends XMLSchema {
|
|
|
14
20
|
|
|
15
21
|
const doc = parent.parser.process (xml)
|
|
16
22
|
|
|
17
|
-
super (parent, XMLSchema.namespaceURI, doc)
|
|
23
|
+
super (parent, XMLSchema.namespaceURI, doc)
|
|
24
|
+
|
|
25
|
+
this.byName = {
|
|
26
|
+
...XSSimpleTypeInteger.byName,
|
|
27
|
+
...XSSimpleTypeFloatingPoint.byName,
|
|
28
|
+
...XSSimpleTypeDT.byName,
|
|
29
|
+
QName : XSSimpleTypeQName,
|
|
30
|
+
boolean : XSSimpleTypeBoolean,
|
|
31
|
+
decimal : XSSimpleTypeDecimal,
|
|
32
|
+
}
|
|
18
33
|
|
|
19
34
|
}
|
|
20
35
|
|
|
21
|
-
getSimpleTypeClass (
|
|
36
|
+
getSimpleTypeClass ({attributes: {name}}) {
|
|
22
37
|
|
|
23
|
-
return
|
|
38
|
+
return this.byName [name] ?? XSSimpleType
|
|
24
39
|
|
|
25
|
-
}
|
|
40
|
+
}
|
|
26
41
|
|
|
27
42
|
}
|
|
28
43
|
|
package/lib/XMLSchemaXml.js
CHANGED
package/lib/XMLSchemata.js
CHANGED
|
@@ -8,7 +8,8 @@ const XMLSchemaXml = require ('./XMLSchemaXml.js')
|
|
|
8
8
|
const XMLSchemaBuiltIn = require ('./XMLSchemaBuiltIn.js')
|
|
9
9
|
const XMLMarshaller = require ('./XMLMarshaller.js')
|
|
10
10
|
|
|
11
|
-
const
|
|
11
|
+
const XSAnyType = require ('./XSAnyType.js')
|
|
12
|
+
const {XSSimpleType} = require ('./simple/XSSimpleType.js')
|
|
12
13
|
|
|
13
14
|
const IDX = Symbol ('_index')
|
|
14
15
|
|
|
@@ -32,6 +33,12 @@ const XMLSchemata = class extends Map {
|
|
|
32
33
|
this.addFile (fn)
|
|
33
34
|
|
|
34
35
|
}
|
|
36
|
+
|
|
37
|
+
getDebugQName (localName, namespaceURI) {
|
|
38
|
+
|
|
39
|
+
return namespaceURI ? `{${namespaceURI}}${localName}` : localName
|
|
40
|
+
|
|
41
|
+
}
|
|
35
42
|
|
|
36
43
|
register (name, targetNamespace) {
|
|
37
44
|
|
|
@@ -51,10 +58,20 @@ const XMLSchemata = class extends Map {
|
|
|
51
58
|
|
|
52
59
|
if (node.localName === 'simpleType' && !node._xsSimpleType) node._xsSimpleType = new (schema.getSimpleTypeClass (node)) (this)
|
|
53
60
|
|
|
61
|
+
if (!('targetNamespace' in node)) node.targetNamespace = namespaceURI
|
|
62
|
+
|
|
54
63
|
return node
|
|
55
64
|
|
|
56
65
|
}
|
|
57
66
|
|
|
67
|
+
getAnyType (node) {
|
|
68
|
+
|
|
69
|
+
if ('_xsAnyType' in node) return node._xsAnyType
|
|
70
|
+
|
|
71
|
+
return node._xsAnyType = new XSAnyType (this, node)
|
|
72
|
+
|
|
73
|
+
}
|
|
74
|
+
|
|
58
75
|
getSimpleType (node) {
|
|
59
76
|
|
|
60
77
|
if ('_xsSimpleType' in node) return node._xsSimpleType
|
|
@@ -65,15 +82,65 @@ const XMLSchemata = class extends Map {
|
|
|
65
82
|
|
|
66
83
|
getSimpleTypeClass (node) {
|
|
67
84
|
|
|
68
|
-
if (node) for (const {localName,
|
|
69
|
-
|
|
70
|
-
|
|
85
|
+
if (node) for (const {localName, attributes: {base}, children} of node.children) switch (localName) {
|
|
86
|
+
|
|
87
|
+
case 'restriction': return this.getType (base)._xsSimpleType.restrict (children)
|
|
88
|
+
|
|
89
|
+
case 'union': return class {
|
|
90
|
+
|
|
91
|
+
constructor (xs) {
|
|
92
|
+
|
|
93
|
+
this.xs = xs
|
|
94
|
+
|
|
95
|
+
this.nodes = children
|
|
96
|
+
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
stringify (v) {
|
|
100
|
+
|
|
101
|
+
const messages = []; for (const node of this.nodes) {
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
|
|
105
|
+
return this.xs.getSimpleType (node).stringify (v)
|
|
106
|
+
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
|
|
110
|
+
messages.push (err.message)
|
|
111
|
+
|
|
112
|
+
}
|
|
71
113
|
|
|
72
|
-
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
throw Error (messages.join ('; '))
|
|
73
117
|
|
|
74
|
-
|
|
118
|
+
}
|
|
75
119
|
|
|
76
|
-
)
|
|
120
|
+
validateScalar (v) {
|
|
121
|
+
|
|
122
|
+
const messages = []; for (const node of this.nodes) {
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
|
|
126
|
+
return this.xs.getSimpleType (node).validateScalar (v)
|
|
127
|
+
|
|
128
|
+
}
|
|
129
|
+
catch (err) {
|
|
130
|
+
|
|
131
|
+
messages.push (err.message)
|
|
132
|
+
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
throw Error (messages.join ('; '))
|
|
138
|
+
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
}
|
|
77
144
|
|
|
78
145
|
return XSSimpleType
|
|
79
146
|
|
|
@@ -98,10 +165,26 @@ const XMLSchemata = class extends Map {
|
|
|
98
165
|
getByReference (ref) {
|
|
99
166
|
|
|
100
167
|
const [localName, namespaceURI] = ref
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
168
|
+
|
|
169
|
+
return this.getSchema (namespaceURI).get (localName)
|
|
170
|
+
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
getSchema (namespaceURI) {
|
|
174
|
+
|
|
175
|
+
if (!this.has (namespaceURI)) throw new Error ('Unknown namespace: ' + namespaceURI)
|
|
176
|
+
|
|
177
|
+
return this.get (namespaceURI)
|
|
178
|
+
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
getTypeDefinition (node) {
|
|
182
|
+
|
|
183
|
+
// while (node.attributes.ref) node = this.getByReference (node.attributes.ref)
|
|
184
|
+
|
|
185
|
+
if (node.attributes.type) return this.getType (node.attributes.type)
|
|
186
|
+
|
|
187
|
+
return node.children.find (i => i.localName.endsWith ('Type'))
|
|
105
188
|
|
|
106
189
|
}
|
|
107
190
|
|
package/lib/XSAnyType.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
const Occurable = require ('./validation/Occurable')
|
|
2
|
+
|
|
3
|
+
module.exports = class {
|
|
4
|
+
|
|
5
|
+
#isAnyAttributeAllowed = false
|
|
6
|
+
#isExtended = false
|
|
7
|
+
#parent = null
|
|
8
|
+
|
|
9
|
+
get isAnyAttributeAllowed () {
|
|
10
|
+
|
|
11
|
+
return this.#isAnyAttributeAllowed
|
|
12
|
+
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
constructor (xs, node) {
|
|
16
|
+
|
|
17
|
+
this.xs = xs
|
|
18
|
+
this.node = node
|
|
19
|
+
|
|
20
|
+
this.attributes = new Map ()
|
|
21
|
+
this.elements = []
|
|
22
|
+
|
|
23
|
+
this.scan (node)
|
|
24
|
+
|
|
25
|
+
if (this.#parent === null) return
|
|
26
|
+
|
|
27
|
+
if (this.#isExtended) this.extend (); else this.restrict ()
|
|
28
|
+
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
extend () {
|
|
32
|
+
|
|
33
|
+
this.attributes = new Map ([
|
|
34
|
+
...this.#parent.attributes,
|
|
35
|
+
...this.attributes
|
|
36
|
+
])
|
|
37
|
+
|
|
38
|
+
this.elements = this.#parent.elements.concat (this.elements)
|
|
39
|
+
|
|
40
|
+
this.content = new Occurable ([this.#parent.content, this.content].filter (Boolean))
|
|
41
|
+
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
restrict () {
|
|
45
|
+
|
|
46
|
+
this.attributes = new Map ([
|
|
47
|
+
...this.#parent.attributes,
|
|
48
|
+
...this.attributes
|
|
49
|
+
])
|
|
50
|
+
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
asSimpleType () {
|
|
54
|
+
|
|
55
|
+
return this.xs.getSimpleType (this.node)
|
|
56
|
+
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
get debugQName () {
|
|
60
|
+
|
|
61
|
+
const {xs, node} = this
|
|
62
|
+
|
|
63
|
+
return xs.getDebugQName (node.attributes.name, node.targetNamespace)
|
|
64
|
+
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
scan (node) {
|
|
68
|
+
|
|
69
|
+
while (node.attributes.ref) node = this.xs.getByReference (node.attributes.ref)
|
|
70
|
+
|
|
71
|
+
switch (node.localName) {
|
|
72
|
+
|
|
73
|
+
case 'anyAttribute': return this.#isAnyAttributeAllowed = true
|
|
74
|
+
|
|
75
|
+
case 'attribute': return this.attributes.set (node.attributes.name, node)
|
|
76
|
+
|
|
77
|
+
case 'any':
|
|
78
|
+
case 'element':
|
|
79
|
+
return this.elements.push (node)
|
|
80
|
+
|
|
81
|
+
case 'all':
|
|
82
|
+
case 'choice':
|
|
83
|
+
case 'sequence':
|
|
84
|
+
this.content ??= new Occurable (node, this.xs)
|
|
85
|
+
break
|
|
86
|
+
|
|
87
|
+
case 'extension':
|
|
88
|
+
this.#isExtended = true
|
|
89
|
+
case 'restriction':
|
|
90
|
+
this.#parent = this.xs.getAnyType (this.xs.getType (node.attributes.base))
|
|
91
|
+
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
for (const child of node.children) this.scan (child)
|
|
95
|
+
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
}
|