* Converts a parsed XML node tree into the JS object shape that the previous * `soap` library produced: nested objects keyed by local element name, * attributes under `attributes`, repeated elements collapsed into arrays, * and pure text nodes returned as strings.
(node: XmlNode)
| 492 | * and pure text nodes returned as strings. |
| 493 | */ |
| 494 | function nodeToValue(node: XmlNode): unknown { |
| 495 | const hasChildren = node.children.length > 0 |
| 496 | const trimmedText = node.text.trim() |
| 497 | const attrKeys = Object.keys(node.attributes).filter( |
| 498 | (k) => k !== 'xmlns' && !k.startsWith('xmlns:') |
| 499 | ) |
| 500 | |
| 501 | if (!hasChildren && attrKeys.length === 0) { |
| 502 | return trimmedText |
| 503 | } |
| 504 | |
| 505 | const obj: Record<string, unknown> = {} |
| 506 | if (attrKeys.length > 0) { |
| 507 | const attrs: Record<string, string> = {} |
| 508 | for (const k of attrKeys) { |
| 509 | const localKey = k.includes(':') ? k.slice(k.indexOf(':') + 1) : k |
| 510 | attrs[localKey] = node.attributes[k] |
| 511 | } |
| 512 | obj.attributes = attrs |
| 513 | } |
| 514 | |
| 515 | if (!hasChildren && trimmedText !== '') { |
| 516 | obj.$value = trimmedText |
| 517 | return obj |
| 518 | } |
| 519 | |
| 520 | for (const child of node.children) { |
| 521 | const key = child.localName |
| 522 | const value = nodeToValue(child) |
| 523 | if (key in obj) { |
| 524 | const existing = obj[key] |
| 525 | if (Array.isArray(existing)) { |
| 526 | existing.push(value) |
| 527 | } else { |
| 528 | obj[key] = [existing, value] |
| 529 | } |
| 530 | } else { |
| 531 | obj[key] = value |
| 532 | } |
| 533 | } |
| 534 | return obj |
| 535 | } |
| 536 | |
| 537 | function findFirst(node: XmlNode, localName: string): XmlNode | null { |
| 538 | if (node.localName === localName) return node |