xml-toolkit 0.0.4 → 0.0.8
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/lib/AttributesMap.js +1 -1
- package/lib/MoxyLikeJsonEncoder.js +11 -5
- package/lib/SAXEvent.js +32 -0
- package/lib/XMLNode.js +13 -0
- package/lib/XMLReader.js +73 -20
- package/package.json +1 -1
- package/test/soap.xml +1 -1
- package/test/test.js +24 -56
package/lib/AttributesMap.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const SAXEvent = require ('./SAXEvent.js')
|
|
1
|
+
const SAXEvent = require ('./SAXEvent.js'), {CHARACTERS, END_ELEMENT} = SAXEvent.TYPES
|
|
2
2
|
|
|
3
3
|
const set = (o, k, nv) => {
|
|
4
4
|
|
|
@@ -12,13 +12,19 @@ const set = (o, k, nv) => {
|
|
|
12
12
|
|
|
13
13
|
const xform = ({children, attributes}) => {
|
|
14
14
|
|
|
15
|
-
let o =
|
|
15
|
+
let o = null
|
|
16
16
|
|
|
17
|
-
if (
|
|
17
|
+
if (attributes != null && attributes.size !== 0) o = Object.fromEntries (attributes.entries ())
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
if (children != null) for (const child of children) switch (child.type) {
|
|
20
20
|
|
|
21
|
-
case
|
|
21
|
+
case CHARACTERS: return child.text
|
|
22
|
+
|
|
23
|
+
case END_ELEMENT:
|
|
24
|
+
|
|
25
|
+
if (o === null) o = {}
|
|
26
|
+
|
|
27
|
+
set (o, child.localName, xform (child))
|
|
22
28
|
|
|
23
29
|
}
|
|
24
30
|
|
package/lib/SAXEvent.js
CHANGED
|
@@ -96,6 +96,20 @@ const SAXEvent = class {
|
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
}
|
|
99
|
+
|
|
100
|
+
get xml () {
|
|
101
|
+
|
|
102
|
+
switch (this.type) {
|
|
103
|
+
|
|
104
|
+
case END_DOCUMENT: return ''
|
|
105
|
+
|
|
106
|
+
case END_ELEMENT: return this.isSelfEnclosed ? '' : `</${this.name}>`
|
|
107
|
+
|
|
108
|
+
default: return this.src
|
|
109
|
+
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
}
|
|
99
113
|
|
|
100
114
|
get attributes () {
|
|
101
115
|
|
|
@@ -182,6 +196,24 @@ const SAXEvent = class {
|
|
|
182
196
|
|
|
183
197
|
}
|
|
184
198
|
|
|
199
|
+
get isStartElement () {
|
|
200
|
+
|
|
201
|
+
return this.type === START_ELEMENT
|
|
202
|
+
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
get isEndElement () {
|
|
206
|
+
|
|
207
|
+
return this.type === END_ELEMENT
|
|
208
|
+
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
get isCharacters () {
|
|
212
|
+
|
|
213
|
+
return this.type === CHARACTERS
|
|
214
|
+
|
|
215
|
+
}
|
|
216
|
+
|
|
185
217
|
}
|
|
186
218
|
|
|
187
219
|
SAXEvent.TYPES = {
|
package/lib/XMLNode.js
CHANGED
|
@@ -25,6 +25,19 @@ const XMLNode = class extends SAXEvent {
|
|
|
25
25
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
cloneStart () {
|
|
29
|
+
|
|
30
|
+
let e = new XMLNode (this.src, this [XML_READER], SAXEvent.TYPES.START_ELEMENT)
|
|
31
|
+
|
|
32
|
+
e [PARENT] = this [PARENT]
|
|
33
|
+
e [LEVEL] = this [LEVEL]
|
|
34
|
+
e [ATTRIBUTES] = this [ATTRIBUTES]
|
|
35
|
+
e [NS_MAP] = this [NS_MAP]
|
|
36
|
+
|
|
37
|
+
return e
|
|
38
|
+
|
|
39
|
+
}
|
|
40
|
+
|
|
28
41
|
get level () {
|
|
29
42
|
|
|
30
43
|
return this [LEVEL]
|
package/lib/XMLReader.js
CHANGED
|
@@ -2,8 +2,8 @@ const assert = require ('assert')
|
|
|
2
2
|
const {Transform} = require ('stream')
|
|
3
3
|
const SAXEvent = require ('./SAXEvent.js')
|
|
4
4
|
const XMLNode = require ('./XMLNode.js')
|
|
5
|
+
const XMLLexer = require ('./XMLLexer.js')
|
|
5
6
|
|
|
6
|
-
const OPT_ONCE = Symbol ('_once')
|
|
7
7
|
const OPT_SRC = Symbol ('_src')
|
|
8
8
|
const OPT_SAX = Symbol ('_sax')
|
|
9
9
|
|
|
@@ -14,7 +14,7 @@ const XMLReader = class extends Transform {
|
|
|
14
14
|
options.decodeStrings = false
|
|
15
15
|
options.objectMode = true
|
|
16
16
|
|
|
17
|
-
if (!('stripSpace' in options)) options.stripSpace =
|
|
17
|
+
if (!('stripSpace' in options)) options.stripSpace = ('filterElements' in options)
|
|
18
18
|
assert (options.stripSpace === true || options.stripSpace === false, 'options.stripSpace must be boolean, not ' + typeof options.stripSpace)
|
|
19
19
|
|
|
20
20
|
if (!('useEntities' in options)) options.useEntities = true
|
|
@@ -23,8 +23,30 @@ const XMLReader = class extends Transform {
|
|
|
23
23
|
if (!('useNamespaces' in options)) options.useNamespaces = true
|
|
24
24
|
assert (options.useNamespaces === true || options.useNamespaces === false, 'options.useNamespaces must be boolean, not ' + typeof options.useNamespaces)
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
let {filterElements} = options; delete options.filterElements; if (filterElements != null) {
|
|
27
|
+
|
|
28
|
+
assert (!('filter' in options), 'filter and filterElements options cannot be set simultaneously')
|
|
29
|
+
|
|
30
|
+
switch (typeof filterElements) {
|
|
31
|
+
|
|
32
|
+
case 'string':
|
|
33
|
+
const localName = filterElements
|
|
34
|
+
filterElements = e => e.localName === localName
|
|
35
|
+
|
|
36
|
+
case 'function':
|
|
37
|
+
options.filter = e => e.type === SAXEvent.TYPES.END_ELEMENT && filterElements (e)
|
|
38
|
+
break
|
|
39
|
+
|
|
40
|
+
default:
|
|
41
|
+
assert.fail ('options.filterElements must be a string or function')
|
|
42
|
+
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let {collect} = options; delete options.collect
|
|
27
48
|
assert (collect == null || typeof collect === 'function', 'options.collect must be a function, not ' + typeof collect)
|
|
49
|
+
if (collect == null && filterElements != null) collect = filterElements
|
|
28
50
|
|
|
29
51
|
const {filter} = options; delete options.filter
|
|
30
52
|
assert (filter == null || typeof filter === 'function', 'options.filter must be a function, not ' + typeof filter)
|
|
@@ -32,19 +54,14 @@ const XMLReader = class extends Transform {
|
|
|
32
54
|
const {map} = options; delete options.map
|
|
33
55
|
assert (map == null || typeof map === 'function', 'options.map must be a function, not ' + typeof map)
|
|
34
56
|
|
|
35
|
-
const {find} = options; delete options.find
|
|
36
|
-
assert (filter == null || find == null, 'filter & find options cannot be set together')
|
|
37
|
-
assert (find == null || typeof find === 'function', 'options.find must be a function, not ' + typeof find)
|
|
38
|
-
|
|
39
57
|
super (options)
|
|
40
58
|
|
|
41
59
|
this.stripSpace = options.stripSpace
|
|
42
60
|
this.useEntities = options.useEntities
|
|
43
61
|
this.useNamespaces = options.useNamespaces
|
|
44
62
|
this.collect = collect || null
|
|
45
|
-
this.filter = filter ||
|
|
63
|
+
this.filter = filter || null
|
|
46
64
|
this.map = map || null
|
|
47
|
-
this [OPT_ONCE] = find != null
|
|
48
65
|
|
|
49
66
|
if (this.useEntities) this.entityResolver = new (require ('./EntityResolver.js')) ()
|
|
50
67
|
|
|
@@ -57,12 +74,56 @@ const XMLReader = class extends Transform {
|
|
|
57
74
|
this [OPT_SAX] = null
|
|
58
75
|
|
|
59
76
|
}
|
|
77
|
+
|
|
78
|
+
async findFirst () {
|
|
79
|
+
|
|
80
|
+
assert (!this.isSAX, 'SAX event subscriber detected, findFirst () cannot be used')
|
|
81
|
+
|
|
82
|
+
return new Promise ((ok, fail) => {
|
|
83
|
+
|
|
84
|
+
this.once ('error', fail)
|
|
85
|
+
|
|
86
|
+
const THE_END = 'finish', nope = () => ok (null)
|
|
87
|
+
|
|
88
|
+
this.on (THE_END, nope)
|
|
89
|
+
|
|
90
|
+
this.on ('data', e => {
|
|
91
|
+
|
|
92
|
+
this.off (THE_END, nope)
|
|
93
|
+
|
|
94
|
+
if (e.type === SAXEvent.TYPES.END_DOCUMENT) return nope ()
|
|
95
|
+
|
|
96
|
+
const src = this [OPT_SRC]; if (src !== null) src.unpipe (this)
|
|
97
|
+
|
|
98
|
+
ok (e)
|
|
99
|
+
|
|
100
|
+
this.destroy ()
|
|
101
|
+
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
process (src, lexerOptions = {}) {
|
|
109
|
+
|
|
110
|
+
const lex = new XMLLexer (lexerOptions)
|
|
111
|
+
|
|
112
|
+
lex.once ('error', x => this.destroy (x))
|
|
113
|
+
|
|
114
|
+
lex.pipe (this)
|
|
115
|
+
|
|
116
|
+
if (Buffer.isBuffer (src) || typeof src === 'string') lex.end (src); else src.pipe (lex)
|
|
117
|
+
|
|
118
|
+
return this
|
|
119
|
+
|
|
120
|
+
}
|
|
60
121
|
|
|
61
122
|
_flush (callback) {
|
|
62
123
|
|
|
63
124
|
this.flush_text ()
|
|
64
125
|
|
|
65
|
-
this.publish (
|
|
126
|
+
this.publish (new XMLNode ('', this), SAXEvent.TYPES.END_DOCUMENT)
|
|
66
127
|
|
|
67
128
|
callback ()
|
|
68
129
|
|
|
@@ -72,7 +133,7 @@ const XMLReader = class extends Transform {
|
|
|
72
133
|
|
|
73
134
|
const v = this [OPT_SAX]; if (v !== null) return v
|
|
74
135
|
|
|
75
|
-
for (const type
|
|
136
|
+
for (const type of Object.values (SAXEvent.TYPES)) if (this.listenerCount (type) !== 0) return this [OPT_SAX] = true
|
|
76
137
|
|
|
77
138
|
return this [OPT_SAX] = false
|
|
78
139
|
|
|
@@ -97,14 +158,6 @@ const XMLReader = class extends Transform {
|
|
|
97
158
|
|
|
98
159
|
}
|
|
99
160
|
|
|
100
|
-
if (this [OPT_ONCE]) {
|
|
101
|
-
|
|
102
|
-
const src = this [OPT_SRC]; if (src !== null) src.unpipe (this)
|
|
103
|
-
|
|
104
|
-
this.destroy ()
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
|
|
108
161
|
}
|
|
109
162
|
|
|
110
163
|
flush_text () {
|
|
@@ -181,7 +234,7 @@ const XMLReader = class extends Transform {
|
|
|
181
234
|
|
|
182
235
|
if (isStart && this.useNamespaces) e.readNamespaces ()
|
|
183
236
|
|
|
184
|
-
this.publish (e)
|
|
237
|
+
this.publish (isStart ? e.cloneStart () : e)
|
|
185
238
|
|
|
186
239
|
if (isStart) {
|
|
187
240
|
|
package/package.json
CHANGED
package/test/soap.xml
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<SOAP-ENV:Header/>
|
|
3
3
|
<SOAP-ENV:Body>
|
|
4
4
|
<SendRequestRequest xmlns="urn://x-artefacts-smev-gov-ru/services/message-exchange/types/1.1" xmlns:ns0="urn://x-artefacts-smev-gov-ru/services/message-exchange/types/basic/1.1">
|
|
5
|
-
<SenderProvidedRequestData Id="Ue7e71ce1-7ce3-4ca5-a689-1a8f2edbb1af">
|
|
5
|
+
<SenderProvidedRequestData Id="Ue7e71ce1-7ce3-4ca5-a689-1a8f2edbb1af" IDDQD="">
|
|
6
6
|
<MessageID>3931cda8-3245-11ec-b0bc-000c293433a0</MessageID>
|
|
7
7
|
<ns0:MessagePrimaryContent>
|
|
8
8
|
<ExportDebtRequestsRequest xmlns="urn:dom.gosuslugi.ru/debt-responses/1.0.0" xmlns:ns0="urn:dom.gosuslugi.ru/common/1.2.0">
|
package/test/test.js
CHANGED
|
@@ -52,79 +52,47 @@ async function test_003_emitter_sync (fn) {
|
|
|
52
52
|
|
|
53
53
|
const xml = fs.readFileSync (
|
|
54
54
|
'test/' + fn
|
|
55
|
-
|
|
55
|
+
, 'utf-8'
|
|
56
56
|
)
|
|
57
57
|
|
|
58
58
|
console.log (xml)
|
|
59
59
|
|
|
60
|
-
const lex = new XMLLexer ({
|
|
61
|
-
// maxLength: 40,
|
|
62
|
-
// encoding: 'ascii',
|
|
63
|
-
// stripSpace: true,
|
|
64
|
-
})
|
|
65
|
-
|
|
66
60
|
const sax = new XMLReader ({
|
|
67
|
-
stripSpace: true,
|
|
68
|
-
|
|
69
|
-
find: e => true
|
|
70
|
-
// && e.type === SAXEvent.TYPES.CHARACTERS
|
|
71
|
-
&& e.localName === 'SendRequestRequest'
|
|
72
|
-
&& e.type === SAXEvent.TYPES.END_ELEMENT
|
|
73
|
-
// && e.level === 6
|
|
74
|
-
,
|
|
61
|
+
// stripSpace: true,
|
|
62
|
+
filterElements: 'SendRequestRequest',
|
|
75
63
|
map: MoxyLikeJsonEncoder ({wrap: 1})
|
|
76
|
-
// useEntities: false,
|
|
77
64
|
})
|
|
78
65
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
66
|
+
/*
|
|
82
67
|
for (let event of [
|
|
83
|
-
'
|
|
84
|
-
'
|
|
85
|
-
'
|
|
86
|
-
'
|
|
87
|
-
'
|
|
88
|
-
'Characters',
|
|
89
|
-
'EndElement',
|
|
90
|
-
'EndDocument',
|
|
68
|
+
'data',
|
|
69
|
+
'close',
|
|
70
|
+
'end',
|
|
71
|
+
'finish',
|
|
72
|
+
// 'EndElement',
|
|
91
73
|
]) sax.on (event, data => {
|
|
92
|
-
|
|
93
|
-
// console.log ([event, data])
|
|
94
|
-
|
|
95
|
-
console.log (JSON.stringify (data, null, 2))
|
|
96
|
-
|
|
97
|
-
/*
|
|
98
|
-
console.log ([event, data, data.name, data.localName, data.namespaceURI])
|
|
99
|
-
|
|
100
|
-
const {attributes} = data; for (const [k, v] of attributes.entries ()) {
|
|
101
|
-
|
|
102
|
-
console.log ([k, attributes.getLocalName (k), attributes.getNamespaceURI (k), v])
|
|
103
|
-
|
|
104
|
-
console.log ([attributes.get ('ns0:foo')])
|
|
105
|
-
|
|
106
|
-
console.log ([attributes.get ('foo')])
|
|
107
74
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
111
|
-
*/
|
|
112
|
-
})
|
|
113
|
-
|
|
75
|
+
console.log ([JSON.stringify (data, null, 2), event])
|
|
114
76
|
|
|
77
|
+
})
|
|
78
|
+
*/
|
|
115
79
|
|
|
80
|
+
|
|
81
|
+
//console.log (sax)
|
|
82
|
+
//console.log (sax.isSAX)
|
|
116
83
|
|
|
117
84
|
/*
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
85
|
+
let s = ''
|
|
86
|
+
for await (const e of sax) {
|
|
87
|
+
s += xml
|
|
88
|
+
// console.log ([e.type, e.isStartElement, e.isEndElement , e.isCharacters])
|
|
89
|
+
}
|
|
121
90
|
*/
|
|
122
|
-
//
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
// for (let c of xml) lexer.write (Buffer.from ([c])); lexer.end ()
|
|
91
|
+
//console.log ([xml, s])
|
|
92
|
+
|
|
93
|
+
const v = await sax.process (xml).findFirst ()
|
|
126
94
|
|
|
127
|
-
|
|
95
|
+
console.log (JSON.stringify (v, null, 2))
|
|
128
96
|
|
|
129
97
|
}
|
|
130
98
|
|