XML - Reading a XML file in Unity - How to do it?

Hey :slight_smile:

Im still very new to all this scripting in UNITY. I want to make a dialogue system to a game I work on.

For me it seems easiest to just make an XML file where I store all the text the dialogue system need to use.

But where I am stuck is to how I read a XML file inside a unity script and then post it out in like a GUI textbox or similar.

Can anyone be so kind to tell me how to read from an XML file?

To store new data into the XML file would be great aswell, but my main concern is to first read from it.

1 Like

the easiest is likely using System.Xml from .NET itself and providing it with the expected data.

Like dreamora mentioned, System.Xml works great for parsing XML. Note though that this will add considerably to the file size (about a megabyte if Iā€™m not mistaken). If download time is a concern and you need something more lightweight, take a look at the ~7 KB XML parser a forum member wrote.

Thanks for your replies :slight_smile:

And yes I think the System.Xml would work great. If I was using C# which I sadly ainā€™t

It has been decided that the game shall be made in JavaScript so I am wondering if I can use something similar to System.Xml in JavaScript or does it have a totally other aproach to it then?

But size and load times aint that big an issue since the game will be build as a standalone application. No web streaming will be made.

That makes no differenceā€¦System.Xml has nothing to do with C#.

ā€“Eric

Doesnā€™t system.xml come from the C# Mono assemblies or?

Oh well I found a way to import system.xml in javascript and I tried make a test loader which I found some help for on a blog.

But it gives me some error I cant figure out fully.

Iā€™ve made a XML filed called xmltest.xml in a folder I call Resources in my Asset folder. (It looks like this)

<?xml version=ā€1.0" encoding=ā€utf-8" ?>
<system>
<item1 id=ā€1" label=ā€helloā€/>
</system>

And then I have attached this script to my main camera

import System.Xml;
import System.IO;

function Start()
{
	var asset:TextAsset = Resources.Load("xmltest");
	if(asset != null)
	{
		var reader:XmlTextReader = new XmlTextReader(new StringReader(asset.text));
		while(reader.Read())
		{
			if(reader.Name == "item1")
			{
				Debug.Log(reader.Name + " label = " + reader.GetAttribute("label"));
			}
		}
	}
}

function Update () {
}

But of some reason I cant figure out it gives me this error when I run it.

ERROR msg:
XmlException: ā€˜"ā€™ or ā€˜ā€™ā€™ is expected. Line 1, position 38.
Mono.Xml2.XmlTextReader.ParseAttributeFromString (System.String src, System.Int32 idx, System.String name, System.String value)
Mono.Xml2.XmlTextReader.VerifyXmlDeclaration ()
Mono.Xml2.XmlTextReader.ReadProcessingInstruction ()
Mono.Xml2.XmlTextReader.ReadContent ()
Mono.Xml2.XmlTextReader.Read ()
System.Xml.XmlTextReader.Read ()
XML-Reader.Start () (at Assets\XML-Reader.js:10)

So yeah I am missing something to make it work fully. Any suggestions?

Ha! See thats weird! When I postet here now I noticed that this forum cant understand all my " characters (in xmltest.xml) so I tried to rewrite them all in the XML document and now it all works!

Lovely! :smile:

Okā€¦ Iā€™ve now tried to move further from my prototype.

I have made this XML document

<?xml version="1.0" encoding="utf-8" ?>

<dialogue>
	<npc1>
		<speach1>Speach bubble 1</speach1>
		<speach2>Speach bubble 2</speach2>
		<speach3>Speach bubble 3</speach3>
	</npc1>
</dialogue>

(This code is made to just try and see how I read the first element and then later I will try implant the SPACE funtion to read the next element)

My script looks like this but i totally wrong I know, but I just cant figure out how to make it.

import System.Xml;
import System.IO;

var StringToBubble : String;

function Start()
{
	var asset:TextAsset = Resources.Load("xmltest");
	if(asset != null)
	{
		var reader:XmlTextReader = new XmlTextReader(new StringReader(asset.text));
		while(reader.Read())
		{
			if(reader.Name == "npc1")
			{				
				StringToBubble = (reader.Name + reader.ReadChars("speach1"));
			}
		}
	}
}

function Update () {
}

function OnGUI  () 
{
	GUI.Box (Rect (75, 75, 100, 20), StringToBubble);
}

And I want to start with reading the text in and when the player press like for example SPACE it goes to read

BUT for now it is only done so you can read and nothing else.

But it gives me this error:
Assets/XML-Reader.js(16,81): BCE0017: The best overload for the method ā€˜System.Xml.XmlTextReader.ReadChars((char), int, int)ā€™ is not compatible with the argument list ā€˜(String)ā€™.

So I have to type something else in the ReadChars() ?

I found this on MSDN about ReadChars()

public function ReadChars(
    buffer : char[], 
    index : int, 
    count : int
) : int

But how to use that IF that is the correct way to read and show large amount of text I dont know.

Anyone know what I am doing wrong and can show me how to do it correct? :slight_smile:

Anyone? :slight_smile: I would really apreciate if one could take a look at this. I seem to be stuck now.

The XML classes in .NET are quite complicated. You may find the XML script mentioned earlier by Matthew to be easier to use. It reads the contents of the file directly into a JavaScript array which you can access using the usual indexing.

1 Like

Donā€™t know if you found your answer, but hereā€™s what I just knocked out. I needed quick and dirty. Iā€™m parsing a private xml file (via WWW), so no validation is needed or wanted. I know what to expect, if I donā€™t get it, I know itā€™s bad.

That said, if anyone wants to correct this slop, feel free to post suggestions here. Iā€™m sure itā€™s overkill and bad form and such. Did this via copy/paste from several sources. I dropped this on an empty GameObject, but perhaps using ā€˜staticā€™ would be better?

import System;
import System.IO;
import System.Xml;
import System.Text;

function Start ()
{
  getXMLdata();
}

public function getXMLdata () 
{
  var i;
Debug.Log("getting...");
  var url = "http://myserver/myscript";
  // Start a download of the given URL
Debug.Log("...from "+url);
  var www : WWW = new WWW (url);
Debug.Log("yielding");
  // Wait for download to complete
  yield www;
Debug.Log("yielded");

  var xml = new XmlDocument();
  xml.LoadXml([url]www.data[/url]);
    
  var wrapperNode = xml.LastChild;
  if (wrapperNode.Name != "wrapper")
  {
    Debug.LogError("This is not a wrapper file");
  }
Debug.Log("got data");

/* Example XML:
<wrapper>
 <myData address="123 Main St." desc="Casa de Joe Shmoe">
  <income>0</income> 
  <bday>Jun 09,1910</bday> 
 </myData>
</wrapper>
*/
  var cnodeCount = wrapperNode.ChildNodes.Count;
Debug.Log("got " +cnodeCount);
  for (i = 0; i < cnodeCount; i++)
  {
    var dataNode = wrapperNode.ChildNodes.Item(i);
    var address = dataNode.Attributes["address"].Value;
    var desc = dataNode.Attributes["desc"].Value;
    var cCount = dataNode.ChildNodes.Count;
    var income = "";
    var bday = "";
    for (var j = 0; j < cCount; j++)
    {
      if (dataNode.ChildNodes.Item(j).Name == "income")
        income = dataNode.ChildNodes.Item(j).InnerText;
      else if (dataNode.ChildNodes.Item(j).Name == "bday")
        bday = dataNode.ChildNodes.Item(j).InnerText;
    }
// Now do something useful with this data
  }
}

In fact, everyone seem to be lazy as me, not reading every pages of unity document :stuck_out_tongue:

Near the bottom of the following page, you will found a small text that mentioned a light weight XML parser that wrote by Novell, the creator of Mono framework. Think that is very reliable too :slight_smile:
http://unity3d.com/support/documentation/Manual/Reducing%20File%20size.html

"Reducing included dlls in the Web Playerā€¦

ā€¦ If you need to parse some Xml files, you can use a smaller xml library like this one Mono.Xml.zip. While most Generic containers are contained in mscorlib, Stack<> and few others are in System.dll. So you really want to avoid those."

I knocked this up for youā€¦
I chnged the xml slightly:

<?xml version="1.0" encoding="utf-8" ?>
<npcs>
   <npc name="Colin" npcType="Barman" entries="3">
      <speach>Speach bubble 1</speach>
      <speach>Speach bubble 2</speach>
      <speach>Speach bubble 3</speach>
   </npc>
</npcs>

I changed it so you could have multiple npcs in one file but youā€™d have to use a string to pick the correct npc data to load, and use a load(npcname) function instead of using Start.

hereā€™s the code, with comments :wink:

import System.Xml;

// npc data
var npcName : String;
var npcType : String;

// chat data
var maxData : int;
var showData : int;
var data : String[];

//simple gui to show read data
function OnGUI()
{
	// ensures I don't try to show data I don't have
	if(showData<maxData)
	{
		GUI.Label(Rect(0,0,200,20), npcType+":"+npcName);
		GUI.Label(Rect(0,20,200,100), data[showData]);
		if(GUI.Button(Rect(0,120,200,20),"Next"))
		{
			// goto next
			showData++;
			// wrap
			if(showData>=maxData)
				showData=0;
		}
	}
}

function Start()
{
	// initialise data
	maxData = 0;
	showData = 0;
	npcName = "unset";
	npcName = "unset";
	data = null;
	
	//readxml from chat.xml in project folder (Same folder where Assets and Library are in the Editor)
      var reader:XmlReader = XmlReader.Create("chat.xml");
	  //while there is data read it
      while(reader.Read())
      {
		//when you find a npc tag do this
         if(reader.IsStartElement("npc"))
         {
			// get attributes from npc tag
            npcName=reader.GetAttribute("name");
			npcType = reader.GetAttribute("npcType");
			maxData = parseInt(reader.GetAttribute("entries"));
			
			//allocate string pointer array
			data = new String[maxData];
			
			//read speach elements (showdata is used instead of having a new int I reset it later)
			for(showData = 0;showData<maxData;showData++)
			{
				reader.Read();
				if(reader.IsStartElement("speach"))
				{
					//fill strings
					data[showData] = reader.ReadString();
				}
			}
			//reset showData index
			showData=0;
         }
      }
	
}

As it says in the code, it loads the xml from the project folder, so you can change the file and not have to create a new build to view it, and if you add the load function you could edit the file while the build is running, and itā€™d read the file each time load is called, so youā€™d see changes as you make them. :wink:

Also easy to change to a resource if you want it that way.

Cool thread - some great resource links and nice ideas!

Hi guys, I thought this might be a good place to ask this question and the answer might help a lot of people out thereā€¦

Here is my xml:

<questions>
	  <page id = "1">
		<ask id = "hex">Hex!</ask>
		<ask id = "battery">Battery.</ask>
		<ask id = "exitconv">Exit Conversation</ask>
         </page>
	 <page id = "2">	
		<ask id = "tents">Tents</ask>
		<ask id = "marshm">Marshmallows</ask>
		<ask id = "roasting">Roasting Stick</ask>
		<ask id = "engage">Engage in friendly conversation.</ask>
		<ask id = "exitconv">Exit Conversation</ask>
          </page>
	 <page id = "3">
		<ask id ="exitconv">Exit Conversation</ask>
	 </page>
	</questions>

here is my attempt at using System.xml which doesnā€™t even close to workā€¦

function Questions() {
	
	var reader : XmlReader = XmlReader.Create("Assets/AssetFiles/CampScene/CamText.xml");

	while (reader.Read())
	{
		
			if(reader.IsStartElement("page")){
				if (parseInt(reader.GetAttribute("id")) == 1) {	
					reader.ReadStartElement("page");
					while (reader.Read()) {
					       if (reader.IsStartElement("ask")) 
                                                      print(reader.ReadString());
					
					 }
					
					
				}	
			}
		
	}
}

Iā€™m trying to figure out how to display only questions that are on a specified page by using the attribute ā€œidā€ i created in the xml to identify which page to grab the questions from. Iā€™ve poured over the .net instructions and I cannot figure this out! Any ideas? My code displays every ā€œaskā€ question from the xml instead of just the ones from a certain ā€œpageā€.
Thanks!

here is my perfect working example :

void Start{	
		XmlDocument doc = new XmlDocument ();
		doc.Load (UnityEngine.Application.dataPath + "/test.xml");
		XmlNode root = doc.FirstChild;
		for (int i = 0; i < root.SelectNodes("descendant::*").Count; i++) {
			if (root.SelectNodes ("descendant::*") [i].Name.ToString ().ToLower ().Contains ("enddate")) {
				string tempString = root.SelectNodes ("descendant::*") [i].InnerText;
				tempString = tempString.Substring (1, 8);
				string newDate = tempString.Substring (2, 2) + "/";
				newDate += tempString.Substring (0, 2) + "/";
				newDate += tempString.Substring (6);
				DateTime licDate = DateTime.Parse (newDate);

				if (DateTime.Today.Date > licDate.Date) {
					//MessageBox.Show("Your lic is expired");
				} else {
					//UnityEngine.Debug.Log("Your lic is running");
				}
			}
			
			if (root.SelectNodes ("descendant::*") [i].Name.ToString ().ToLower ().Contains ("coursepackname")) {
				coursePackName.Add(root.SelectNodes ("descendant::*") [i].InnerXml);
//				print (root.SelectNodes ("descendant::*") [i].InnerXml);
			}
		}

And Here is the xml:

<NewDataSet>
  <dtCoursePack>
    <LicenseId>0808201210377141</LicenseId>
    <StartDate>1080820120310</StartDate>
    <EndDate>1150820120310</EndDate>
    <CoursePackName>UNI-100-000-S-HS-1234</CoursePackName>
    <CoursePackDescription>Universal Stereo High Sch</CoursePackDescription>
    <Version>UNI-100-812-ST-HS-1234</Version>
  </dtCoursePack>
  <dtCoursePack>
    <LicenseId>0808201210377141</LicenseId>
    <StartDate>1080820120310</StartDate>
    <EndDate>1150820120310</EndDate>
    <CoursePackName>UNI-100-000-S-MS-1234</CoursePackName>
    <CoursePackDescription>Universal Stereo Middle Sch</CoursePackDescription>
    <Version>UNI-100-812-ST-MS-1234</Version>
  </dtCoursePack>
  <dtCoursePack>
    <LicenseId>0808201210377141</LicenseId>
    <StartDate>1080820120310</StartDate>
    <EndDate>1150820120310</EndDate>
    <CoursePackName>UNI-100-000-S-SS-1234</CoursePackName>
    <CoursePackDescription>Universal Stereo Secondry School</CoursePackDescription>
    <Version>UNI-100-812-ST-SS-1234</Version>
  </dtCoursePack>
</NewDataSet>
1 Like

here is a solution I came up with if it helps anyoneā€¦

var doc : XmlDocument = new XmlDocument();
	doc.Load("Assets/AssetFiles/CampScene/CamText.xml");
	
	var nodeList : XmlNodeList;
	var root : XmlNode = doc.DocumentElement;
	
	nodeList = root.SelectNodes("descendant::page[@id='1']");  //this selects all nodes with page id="1"
	
	nodeList = nodeList[0].ChildNodes;  //this rebuilds the list using all the childnodes from the nodes with page id ="1"
	
		
	var i : int;
	for (i=0; i<nodeList.Count; i++) {
		print(nodeList[i].Attributes["id"].Value);  
		print(nodeList[i].InnerText);  
		
	}

Stoping with the difficult code.
I coded my XML this way and work perfect

using UnityEngine;
using System.Collections;
using System.Xml;
using System.IO;

public class Interface : MonoBehaviour {
	
	// Use this for initialization
	
	void Start () 
	{
		
		var xml = new XmlDocument();
		if (Application.systemLanguage.ToString() == "Portuguese"){
		  	xml.Load(UnityEngine.Application.dataPath + "/XMLS/PT-BR.xml");
		}else{
			xml.Load(UnityEngine.Application.dataPath + "/XMLS/EN.xml");
		}
		print (xml.ChildNodes[0].ChildNodes[1].InnerText);
		
		
		
	}
	
	
	// Update is called once per frame
	void Update () {
	
	}
}

Unity Community - Y U No Use Serialization

using System.Xml.Serialization;

namespace Kelso.UnityForum
{
	[XmlRoot(ElementName = "questions")]
	public class Questions
	{
		[XmlElement(ElementName = "page")]
		public Page[] Pages { get; set; }
	}
	
	public class Page
	{
		[XmlAttribute(AttributeName = "id")]
		public int Id { get; set; }
	
		[XmlElement(ElementName = "ask")]
		public Ask[] Asks { get; set; }
	}
	
	public class Ask
	{
		[XmlAttribute(AttributeName = "id")]
		public string Id { get; set; }
		
		[XmlText(Type = typeof(string))]
		public string Value { get; set; }
	}
}
using System;
using System.IO;
using System.Xml.Serialization;
using UnityEngine;
using Kelso.UnityForum;

public class Loader : MonoBehaviour
{
	private Questions questions;
	
	void Start()
	{
		XmlSerializer serial = new XmlSerializer(typeof(Questions))]
		Stream reader = new FileStream("test.xml", FileMode.Open);
		questions = (Questions)serial.Deserialize(reader);
	}
}
3 Likes

Just wondering how youā€™d implement the above into a working dialogue system- how youā€™d call the relevant questions when needed?