93 lines
3.6 KiB
GDScript3
93 lines
3.6 KiB
GDScript3
|
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 </%s> 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
|