TinyXmlReader

From Unify Community Wiki
Jump to: navigation, search

You can use Mono's System.Xml for handling XML files but this requires including the System.Xml dll into your Unity program which increases its file size by about 1 MB. Not to mention the lack of documentation for using System.Xml on UnityScript.

I found rolling my own XML parser was easier. Note however that this is a really simple XML parser, it doesn't recognize attributes (I did not implement it simply because I don't use XML attributes).

TinyXmlReader.cs

using UnityEngine;
using System.Collections;
 
public class TinyXmlReader
{
    private string xmlString = "";
    private int idx = 0;
 
    public TinyXmlReader(string newXmlString)
    {
        xmlString = newXmlString;
    }
 
    public string tagName = "";
    public bool isOpeningTag = false;
    public string content = "";
 
 
	// properly looks for the next index of _c, without stopping at line endings, allowing tags to be break lines	
	int IndexOf(char _c, int _i)
	{
		int i = _i;
		while (i < xmlString.Length)
		{
			if (xmlString[i] == _c)
				return i;
 
			++i;
		}
 
		return -1;
	}
 
    public bool Read()
    {
		if (idx > -1)
        	idx = xmlString.IndexOf("<", idx);
 
        if (idx == -1)
        {
            return false;
        }
        ++idx;
 
		// skip attributes, don't include them in the name!
		int endOfTag = IndexOf('>', idx);
		int endOfName = IndexOf(' ', idx);
        if ((endOfName == -1) || (endOfTag < endOfName))
	    {
			endOfName = endOfTag;
		}
 
		if (endOfTag == -1)
        {
            return false;
        }
 
        tagName = xmlString.Substring(idx, endOfName - idx);
 
        idx = endOfTag;
 
        // check if a closing tag
        if (tagName.StartsWith("/"))
        {
            isOpeningTag = false;
            tagName = tagName.Remove(0, 1); // remove the slash
        }
        else
        {
            isOpeningTag = true;
        }
 
        // if an opening tag, get the content
        if (isOpeningTag)
        {
            int startOfCloseTag = xmlString.IndexOf("<", idx);
            if (startOfCloseTag == -1)
            {
                return false;
            }
 
            content = xmlString.Substring(idx+1, startOfCloseTag-idx-1);
            content = content.Trim();
        }
 
        return true;
    }
 
    // returns false when the endingTag is encountered
    public bool Read(string endingTag)
    {
        bool retVal = Read();
        if (tagName == endingTag && !isOpeningTag)
        {
            retVal = false;
        }
        return retVal;
    }
}

TinyXmlReader.js

class TinyXmlReader
{
 
private var xmlString = "";
private var idx = 0;
 
function TinyXmlReader(aXmlString : String)
{
	xmlString = aXmlString;
}
 
var tagName = "";
var isOpeningTag = false;
var content = "";
 
function Read() : boolean
{
	idx = xmlString.IndexOf("<", idx);
	if (idx == -1)
	{
		return false;
	}
	++idx;
 
	var endOfTag = xmlString.IndexOf(">", idx);
	if (endOfTag == -1)
	{
		return false;
	}
 
	tagName = xmlString.Substring(idx, endOfTag-idx);
 
	idx = endOfTag;
 
	// check if a closing tag
	if (tagName.StartsWith("/"))
	{
		isOpeningTag = false;
		tagName = tagName.Remove(0, 1); // remove the slash
	}
	else
	{
		isOpeningTag = true;
	}
 
	// if an opening tag, get the content
	if (isOpeningTag)
	{
		var startOfCloseTag = xmlString.IndexOf("<", idx);
		content = xmlString.Substring(idx+1, startOfCloseTag-idx-1);
		content = content.Trim();
	}
 
	return true;
}
 
// returns false when the endingTag is encountered
function Read(endingTag : String) : boolean
{
	var retVal = Read();
	if (tagName == endingTag && !isOpeningTag)
	{
		retVal = false;
	}
	return retVal;
}
 
}

Usage

Here I provide example code on how to use the TinyXmlReader.

private var text : String;
var skin : GUISkin;
 
function OnGUI()
{
	GUILayout.Label(text, skin.label);
}
 
function Start()
{
	var xmlText = System.IO.File.ReadAllText(Application.dataPath + "/Rifleman.xml");
	var reader = TinyXmlReader(xmlText);
	while (reader.Read())
	{
		if (reader.isOpeningTag)
		{
			text += (reader.tagName + " \"" + reader.content + "\"\n");
		}
		if (reader.tagName == "Skills" && reader.isOpeningTag)
		{
			while(reader.Read("Skills")) // read as long as not encountering the closing tag for Skills
			{
 
				if (reader.isOpeningTag)
				{
					text += ("Skill: " + reader.tagName + " \"" + reader.content + "\"\n");
				}
			}
		}
	}
}

Plus, here's the XML file used for the example code: <xml> <?xml version="1.0" encoding="UTF-8"?> <Unit> <Type>Rifleman</Type> <Label>ライフル銃兵 é (для Windows тоже!)</Label> <MaxHP>100</MaxHP> <SightRange>10</SightRange> <Skills> <UnitMovement> <Label>Move</Label> <Animation>move</Animation> <Range>10</Range> <Speed>6</Speed> </UnitMovement> <SimpleAttack> <Label>Attack</Label> <Animation>shoot</Animation> <Range>10</Range> <Damage>20</Damage> <AttackTime>0.348</AttackTime> <AttackType>Ranged</AttackType> </SimpleAttack> <Grenade> <Label>Throw Grenade</Label> <Animation>shoot</Animation> <Range>10</Range> <Damage>20</Damage> <AttackTime>0.348</AttackTime> <ExplosionRadius>5</ExplosionRadius> <NoOfUses>3</NoOfUses> </Grenade> </Skills> <Pahabol>oompa loompa doompity doo, we wouldn't hit that and neither should you</Pahabol> </Unit> </xml>

It produces this output:

TinyXmlReader Sample Output

Personal tools
Namespaces

Variants
Actions
Navigation
Extras
Toolbox