| 14 | }; |
| 15 | |
| 16 | const serializeXml = ( |
| 17 | node: ChildNode & { attributes?: NamedNodeMap } |
| 18 | ): SerializedXMLObject | string | undefined => { |
| 19 | const { nodeName, nodeType, nodeValue } = node; |
| 20 | |
| 21 | // text |
| 22 | if (nodeType === 3) { |
| 23 | return nodeValue || undefined; |
| 24 | } |
| 25 | |
| 26 | // comment, ignore |
| 27 | if (nodeType === 8) { |
| 28 | return undefined; |
| 29 | } |
| 30 | |
| 31 | const children = Array.from(node.childNodes).map((child) => |
| 32 | serializeXml(child) |
| 33 | ); |
| 34 | |
| 35 | const attributes = |
| 36 | node.attributes && |
| 37 | Array.from(node.attributes).reduce( |
| 38 | (acc: {}, attr: any) => ({ ...acc, [attr.name]: attr.value }), |
| 39 | {} |
| 40 | ); |
| 41 | |
| 42 | let childObject: any = {}; |
| 43 | |
| 44 | if (children.length === 1 && typeof children[0] === "string") { |
| 45 | childObject[nodeName] = children[0]; |
| 46 | } else { |
| 47 | childObject[nodeName] = {}; |
| 48 | |
| 49 | // childenUniqueKeys check if children should be processed as array |
| 50 | // or should be added as properties of parent object. |
| 51 | // e.g: In [{ name: 'foo' }, { name: 'bar' }], |
| 52 | // children bear the same "name" key, so parent object will look like: |
| 53 | // |
| 54 | // parent: { |
| 55 | // $values: [ |
| 56 | // { name: 'foo' }, |
| 57 | // { name: 'bar' } |
| 58 | // ], |
| 59 | // $attributes: { ... } |
| 60 | // } |
| 61 | // |
| 62 | // In [{ name: 'foo' }, { age: 10 }], |
| 63 | // children have different keys and will be merged into parent object: |
| 64 | // parent: { name: 'foo', age: 10 } |
| 65 | const childenUniqueKeys = new Set( |
| 66 | children.map((child: any) => Object.keys(child)[0]) |
| 67 | ); |
| 68 | |
| 69 | if (childenUniqueKeys.size === children.length) { |
| 70 | childObject[nodeName] = children.reduce( |
| 71 | (acc: {}, child: any) => ({ ...acc, ...child }), |
| 72 | {} |
| 73 | ); |