Skip to content

Godot:XMLParser

This class can serve as base to make custom XML parsers. Since XML is a very flexible standard, this interface is low-level so it can be applied to any possible schema.

GDScript example

# GDScript 문법이다.
extends XMLParser
class_name XmlDeserializer

export var tag_name : String setget , get_tag_name
export var node_type: String setget, get_node_type_string

func get_tag_name():
    if get_node_type() == XMLParser.NODE_TEXT:
        return "[Text]"
    return get_node_name()

var err = OK
var current_file_name
func read_start_element() -> String:
    if is_empty() and get_node_type() == XMLParser.NODE_ELEMENT:
        return ""
    err = read()
    while err == OK:
        match get_node_type():
            XMLParser.NODE_ELEMENT:
                return get_node_name()
            XMLParser.NODE_ELEMENT_END:
                return ""
            _:
                pass
        err = read()
    return ""

func skip():
    if is_empty():
        err = read()
    else:
        skip_section()

func attr(name) -> String:
    return get_named_attribute_value(name)

func init_attribute(node, name, xml_name = null):
    if not xml_name:
        xml_name = name
    var old_value = node.get_indexed(name)
    var value = read_typed_value(typeof(old_value), xml_name)
    assert(value != null)
    node.set_indexed(name, value)

func read_typed_value(type, attribute):
    var xml_val = get_named_attribute_value(attribute)
    match type:
        TYPE_BOOL:
            return xml_val.to_lower() == "true"
        TYPE_INT:
            return xml_val.to_int()
        TYPE_REAL:
            return xml_val.to_float()
        TYPE_STRING:
            return xml_val.xml_unescape()
        _:
            if xml_val == "null":
                return null
            else:
                error("Cannot initialize property: Unknown type: " + String(type))
                return null

func init_property(node, prop_name = null):
    assert(get_node_type() == XMLParser.NODE_ELEMENT)
    assert(get_node_name() == "property")
    prop_name = Util.opt(prop_name, attr("name"))
    var old_value = node.get_indexed(prop_name)
    var value
    if has_attribute("value"):
        assert(old_value != null)
        value = read_typed_value(typeof(old_value), "value")
        assert(value != null)
        skip()
    else:
        value = read_value()
        assert(value != null)
        skip()

    node.set(prop_name, value)

func read_value():
    if not read_start_element() == "value":
        error("Malformed property tag")
        return null
    var value = parse_value()
    return value

func parse_value():
    match attr("type"):
        "Color":
            var value = Color(attr("r").to_float(), attr("g").to_float(), attr("b").to_float(), attr("a").to_float())
            skip()
            return value
        "Vector2":
            var value = Vector2(attr("x").to_float(), attr("y").to_float())
            skip()
            return value
        "Resource":
            var value = load(attr("value"))
            skip()
            return value
        "Rect2":
            var value = Rect2(attr("x").to_float(), attr("y").to_float(), attr("w").to_float(), attr("h").to_float())
            skip()
            return value
        "int":
            var value = attr("value").to_int()
            skip()
            return value
        "real":
            var value = attr("value").to_float()
            skip()
            return value
        "bool":
            var value =  attr("value").to_lower() == "true"
            skip()
            return value
        "string":
            var value =  attr("value").xml_unescape()
            skip()
            return value
        "Array":
            var res =[]
            var element = read_start_element()
            while element == "value":
                res.append(parse_value())
                element = read_start_element()
            return res

func read_text():
    assert(get_node_type() == NODE_ELEMENT)
    err = read()
    var text = get_node_data()
    err = read()
    assert(get_node_type() == NODE_ELEMENT_END)
    return text

func get_node_type_string():
    match get_node_type():
        NODE_NONE: return "NONE"
        NODE_ELEMENT: return "ELEMENT"
        NODE_ELEMENT_END: return "END_ELEMENT"
        NODE_TEXT: return "TEXT"
        NODE_COMMENT: return "COMMENT"
        NODE_CDATA: return "CDATA"
        NODE_UNKNOWN: return "UNKNOWN"

func read_world(file_name):
    current_file_name = file_name
    err = open(file_name)
    if err != OK:
        return

    err = read()
    var root = read_start_element()
    if root != "save_game":
        return null
    if not read_start_element() == "world":
        return null
    var world = load("res://world.tscn").instance()
    Debug.dump_tree(world)
    world.deserialize(self)

    return world

func deserialize_children(node: Object, prefix="deserialize_"):
    if is_empty():
        skip()
        return
    assert(get_node_type() == NODE_ELEMENT)
    var tag = get_node_name()
    var element = read_start_element()
    while element:
        node.call(prefix+element, self)
        element = read_start_element()
    assert(get_node_type() == NODE_ELEMENT_END and get_node_name() == tag or is_empty())

func trace(msg = ""):
    print("%s:%d %s:%s %s" % [current_file_name, get_current_line(), get_node_type_string(), get_node_name(), msg])

func warning(text):
    push_warning("%s(%d): %s" % [current_file_name, get_current_line(), text])

func error(text):
    push_error("%s(%d): %s" % [current_file_name, get_current_line(), text])

Godot XMLParser C# class sample

using Godot;
using System;
using System.IO;
using System.Text;
using System.Collections.Generic;
using System.Diagnostics;


public class XmlReader
{
    public class Node
    {
        public readonly string tag;
        public readonly bool isRoot;

        public Dictionary<string, string> attributes = new Dictionary<string, string>();
        public List<Node> children = new List<Node>();
        public string text = "";

        public Node()
        {
            this.tag = "";
            this.isRoot = true;
        }

        public Node(string tag)
        {
            this.tag = tag;
            this.isRoot = false;
        }

        public bool IsRoot()
        {
            return this.isRoot;
        }

        public string ToPrintableArguments()
        {
            using (MemoryStream stream = new MemoryStream())
            {
                using (StreamWriter writer = new StreamWriter(stream))
                {
                    writer.Write("[");
                    foreach (KeyValuePair<string, string> attr in attributes)
                    {
                        writer.Write($"{attr.Key}:{attr.Value},");
                    }
                    writer.Write("]");
                }
                return Encoding.UTF8.GetString(stream.ToArray());
            }
        }

        public void WritePrintableInfo(StreamWriter writer, string marginLeft = " ")
        {
            writer.WriteLine(marginLeft + tag + ToPrintableArguments() + ": " + this.text);
            foreach (var node in children)
            {
                node.WritePrintableInfo(writer, marginLeft + " ");
            }
        }

        public string ToPrintableText()
        {
            using (MemoryStream stream = new MemoryStream())
            {
                using (StreamWriter writer = new StreamWriter(stream))
                {
                    WritePrintableInfo(writer);
                }
                return Encoding.UTF8.GetString(stream.ToArray());
            }
        }
    }

    private Godot.XMLParser _parser = new Godot.XMLParser();

    public XmlReader()
    {
    }

    public bool OpenXmlFile(string xmlPath)
    {
        return _parser.Open(xmlPath) == Godot.Error.Ok;
    }

    public bool OpenXmlText(string xmlText)
    {
        return OpenXmlUtf8Bytes(System.Text.Encoding.UTF8.GetBytes(xmlText));
    }

    public bool OpenXmlUtf8Bytes(byte [] xmlUtf8Bytes)
    {
        return _parser.OpenBuffer(xmlUtf8Bytes) == Godot.Error.Ok;
    }

    private Godot.XMLParser.NodeType GetCurrentNodeType()
    {
        return _parser.GetNodeType();
    }

    private bool IsCurrentElement()
    {
        return GetCurrentNodeType() == Godot.XMLParser.NodeType.Element;
    }

    private bool IsCurrentElementEnd()
    {
        return GetCurrentNodeType() == Godot.XMLParser.NodeType.ElementEnd;
    }

    private bool IsCurrentText()
    {
        return GetCurrentNodeType() == Godot.XMLParser.NodeType.Text;
    }

    private bool IsCurrentComment()
    {
        return GetCurrentNodeType() == Godot.XMLParser.NodeType.Comment;
    }

    private bool IsCurrentCdata()
    {
        return GetCurrentNodeType() == Godot.XMLParser.NodeType.Cdata;
    }

    private bool IsCurrentUnknown()
    {
        return GetCurrentNodeType() == Godot.XMLParser.NodeType.Unknown;
    }

    private string GetCurrentNodeTypeName()
    {
        switch (_parser.GetNodeType())
        {
            case Godot.XMLParser.NodeType.Element:
                return "Element";
            case Godot.XMLParser.NodeType.ElementEnd:
                return "ElementEnd";
            case Godot.XMLParser.NodeType.Text:
                return "Text";
            case Godot.XMLParser.NodeType.Comment:
                return "Comment";
            case Godot.XMLParser.NodeType.Cdata:
                return "Cdata";
            case Godot.XMLParser.NodeType.Unknown:
                return "Unknown";
            default:
                throw new IndexOutOfRangeException();
        }
    }

    private string GetCurrentTagName()
    {
        Debug.Assert(IsCurrentElement());
        return _parser.GetNodeName();
    }

    private void ReadFirstElement()
    {
        while (_parser.Read() == Godot.Error.Ok)
        {
            if (_parser.GetNodeType() == Godot.XMLParser.NodeType.Element)
            {
                return;
            }
        }
    }

    private Node CreateNode(string tag = "")
    {
        Node node = new Node(tag);
        bool done = false;

        int attributeNum = _parser.GetAttributeCount();
        for (int i = 0; i < attributeNum; ++i)
        {
            var key = _parser.GetAttributeName(i);
            var val = _parser.GetAttributeValue(i);
            node.attributes.Add(key, val);
        }

        if (_parser.IsEmpty())
        {
            return node;
        }

        while (_parser.Read() == Godot.Error.Ok)
        {
            switch (_parser.GetNodeType())
            {
                case Godot.XMLParser.NodeType.Element:
                    node.children.Add(CreateNode(_parser.GetNodeName()));
                    break;
                case Godot.XMLParser.NodeType.ElementEnd:
                    done = true;
                    break;
                case Godot.XMLParser.NodeType.Text:
                    node.text = _parser.GetNodeData().Trim();
                    break;
                case Godot.XMLParser.NodeType.Comment:
                case Godot.XMLParser.NodeType.Cdata:
                case Godot.XMLParser.NodeType.Unknown:
                    break;
                default:
                    throw new NotImplementedException();
            }

            if (done)
            {
                break;
            }
        }

        return node;
    }

    public Node Read()
    {
        return CreateNode();
    }
}

See also

Favorite site