extends Node class XmlNode extends Reference: func is_element() -> bool: return false func is_text() -> bool: return false class XmlElement extends XmlNode: var node_name: String var attributes: Dictionary # String to String var children: Array # of XmlNode func is_element() -> bool: return true func take_named_child_element(p_node_name: String) -> XmlElement: for idx in range(self.children.size()): var child := self.children[idx] as XmlElement if child != null and child.node_name == p_node_name: self.children.remove(idx) return child return null func as_string(level = 0) -> String: var result = '\t'.repeat(level) + "XmlElement \"%s\"" % self.node_name if self.attributes.size() > 0: result += ", Attributes: " + String(self.attributes) for child in self.children: result += '\n' + '\t '.repeat(level) + child.as_string(level + 1) return result class XmlText extends XmlNode: var data: String func is_text() -> bool: return true func as_string(level = 0) -> String: return "XmlText \"%s\"" % self.data ## Wrapper over XMLParser for TCP stream oriented XML data. # todo: Save namespaces. class Parser extends Reference: var root: XmlElement = null var _element_stack: Array # of XmlElement ## Returns true if root element is closed, otherwise more tags are expected to come later. func parse_a_bit(data): # -> bool or Error: var error: int var parser := XMLParser.new() if data is String: data = data.to_utf8() error = parser.open_buffer(data) if error != OK: push_error("Error opening a buffer for XML parsing") return error while parser.read() == OK: if parser.get_node_type() == XMLParser.NODE_ELEMENT: var element := XmlElement.new() if self.root != null: self._element_stack[self._element_stack.size() - 1].children.push_back(element) else: self.root = element if not parser.is_empty(): self._element_stack.push_back(element) element.node_name = parser.get_node_name() var attribute_count := parser.get_attribute_count() for idx in range(attribute_count): element.attributes[parser.get_attribute_name(idx)] = parser.get_attribute_value(idx) elif parser.get_node_type() == XMLParser.NODE_ELEMENT_END: if self._element_stack.size() == 0: push_error("Closing element closes nothing" % [parser.get_node_name()]) return ERR_PARSE_ERROR var popped := self._element_stack.pop_back() as XmlElement if popped.node_name != parser.get_node_name(): push_error("Element <%s> closes sooner than <%s>" % [parser.get_node_name(), popped.node_name]) return ERR_PARSE_ERROR elif parser.get_node_type() == XMLParser.NODE_TEXT: if self._element_stack.size() == 0: push_error("Text node should be a child of an element") return ERR_PARSE_ERROR var text = XmlText.new() text.data = parser.get_node_data() self._element_stack[self._element_stack.size() - 1].children.push_back(text) else: push_warning("Node type unimplemented and ignored") return self._element_stack.size() == 0