xml-parser.ts 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. /**
  2. * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. */
  6. export type XMLNodeAttributes = { [k: string]: any }
  7. export interface XMLNode {
  8. name?: string
  9. content?: string
  10. attributes: XMLNodeAttributes
  11. children?: XMLNode[]
  12. }
  13. export interface XMLDocument {
  14. declaration?: XMLNode,
  15. root?: XMLNode
  16. }
  17. export function getXMLNodeByName(name: string, children: XMLNode[]) {
  18. for (let i = 0, il = children.length; i < il; ++i) {
  19. if (children[i].name === name) return children[i]
  20. }
  21. }
  22. const reStrip = /^['"]|['"]$/g
  23. const reTag = /^<([\w-:.]+)\s*/
  24. const reContent = /^([^<]*)/
  25. const reAttr = /([\w:-]+)\s*=\s*("[^"]*"|'[^']*'|\w+)\s*/
  26. function strip (val: string) {
  27. return val.replace(reStrip, '')
  28. }
  29. /**
  30. * Simple XML parser
  31. * adapted from https://github.com/segmentio/xml-parser (MIT license)
  32. */
  33. export function parseXml (xml: string): XMLDocument {
  34. // trim and strip comments
  35. xml = xml.trim().replace(/<!--[\s\S]*?-->/g, '')
  36. return document()
  37. function document () {
  38. return {
  39. declaration: declaration(),
  40. root: tag()
  41. }
  42. }
  43. function declaration () {
  44. const m = match(/^<\?xml\s*/)
  45. if (!m) return
  46. // tag
  47. const node: XMLNode = {
  48. attributes: {}
  49. }
  50. // attributes
  51. while (!(eos() || is('?>'))) {
  52. const attr = attribute()
  53. if (!attr) return node
  54. node.attributes[attr.name] = attr.value
  55. }
  56. match(/\?>\s*/)
  57. return node
  58. }
  59. function tag () {
  60. const m = match(reTag)
  61. if (!m) return
  62. // name
  63. const node: XMLNode = {
  64. name: m[1],
  65. attributes: {},
  66. children: []
  67. }
  68. // attributes
  69. while (!(eos() || is('>') || is('?>') || is('/>'))) {
  70. const attr = attribute()
  71. if (!attr) return node
  72. node.attributes[attr.name] = attr.value
  73. }
  74. // self closing tag
  75. if (match(/^\s*\/>\s*/)) {
  76. return node
  77. }
  78. match(/\??>\s*/)
  79. // content
  80. node.content = content()
  81. // children
  82. let child
  83. while ((child = tag())) {
  84. node.children!.push(child)
  85. }
  86. // closing
  87. match(/^<\/[\w-:.]+>\s*/)
  88. return node
  89. }
  90. function content () {
  91. const m = match(reContent)
  92. if (m) return m[1]
  93. return ''
  94. }
  95. function attribute () {
  96. const m = match(reAttr)
  97. if (!m) return
  98. return { name: m[1], value: strip(m[2]) }
  99. }
  100. function match (re: RegExp) {
  101. const m = xml.match(re)
  102. if (!m) return
  103. xml = xml.slice(m[0].length)
  104. return m
  105. }
  106. function eos () {
  107. return xml.length === 0
  108. }
  109. function is (prefix: string) {
  110. return xml.indexOf(prefix) === 0
  111. }
  112. }