- Home /
How to read data with 'Saving and Loading Data : XmlSerializer'
I'm following the guide on the Unity Wiki : http://wiki.unity3d.com/index.php?title=Saving_and_Loading_Data:_XmlSerializer
I've created my monsters.xml, Monster.js and MonsterContainer.js and there are no errors.
Now in my test script :
var dragonName : String = "EarthDragon";
var baseHealth : int = 0;
var baseAttack : int = 0;
var baseDefense : int = 0;
function Start()
{
var monsterCollection : MonsterCollection = MonsterContainer.Load( Path.Combine( Application.dataPath, "monsters.xml" ) );
}
this gives the error :
Assets/_Scripts/XmlTest.js(48,33): BCE0018: The name 'MonsterCollection' does not denote a valid type ('not found').
Now I understand there is no class named MonsterCollection therefore the type doesn't exist, but I'm following a 'tutorial', I've never done this before and frankly don't know what I'm supposed to do. Is this thing supposed to recognize MonsterCollection as the root element of the xml? Am I missing a class?
My goal is to read an Attribute of the root element by name, then populate my variables based on the values in nested elements under that Attribute. How can I achieve this?
The examples found here are using a completely different method to the wiki. The question here suggests using an available package, but I'm coding in uJS. Can anyone help me with this problem, understand why the guide is failing, and how I can populate my variables based on a name to reference an attribute (child element)? Thanks.
Further information :
monster.xml
<MonsterCollection>
<Monsters>
<Monster name="EarthDragon">
<Health>45</Health>
<Attack>49</Attack>
<Defense>49</Defense>
</Monster>
<Monster name="FireDragon">
<Health>39</Health>
<Attack>52</Attack>
<Defense>43</Defense>
</Monster>
</Monsters>
</MonsterCollection>
Monster.js
import System.Xml;
import System.Xml.Serialization;
public class Monster
{
@XmlAttribute("name")
public var name : String;
public var health : int;
public var attack : int;
public var defense : int;
}
MonsterContainer.js
import System.Collections.Generic;
import System.Xml;
import System.IO;
@XmlRoot("MonsterCollection")
public class MonsterContainer
{
@XmlArray("Monsters")
@XmlArrayItem("Monster")
public var Monsters : List.< Monster > = new List.< Monster >();
public function Save( path : String )
{
var serializer : XmlSerializer = new XmlSerializer( MonsterContainer );
var stream : Stream = new FileStream( path, FileMode.Create );
serializer.Serialize( stream, this );
stream.Close();
}
public static function Load( path : String ) : MonsterContainer
{
var serializer : XmlSerializer = new XmlSerializer( MonsterContainer );
var stream : Stream = new FileStream( path, FileMode.Open );
var result : MonsterContainer = serializer.Deserialize( stream ) as MonsterContainer;
stream.Close();
return result;
}
//Loads the xml directly from the given string. Useful in combination with www.text.
public static function LoadFromText( text : String ) : MonsterContainer
{
var serializer : XmlSerializer = new XmlSerializer(MonsterContainer);
return serializer.Deserialize(new StringReader(text)) as MonsterContainer;
}
}
Is there anyone on this website who is familiar with reading and writing to xml files?
I understand the process, just need the syntax.
Load the xml file to a temporary variable
read through the attributes of that temporary file (locate the key)
when the key is found, read all the nested elements under that key, storing each in my global variables
I'm more than capable of using StreamReader to read a whole file, and extract the information from there, but I thought this would be an easier way to look up a key and grab just a portion of the elements under that key. The guide I'm following has no mention of the type $$anonymous$$onsterCollection. How am I supposed to be reading the xml, finding the key, and extracting the sub-elements under that key?
For example, I can get this to work
var doc : XmlDocument = new XmlDocument();
var path : String = Path.Combine( Application.dataPath, "monsters2.xml" );
doc.Load( path );
var nodeList : XmlNodeList;
var root : XmlNode = doc.DocumentElement;
nodeList = root.SelectNodes("$$anonymous$$onsters"); //this selects all nodes $$anonymous$$onsters
nodeList = nodeList[0].ChildNodes; //this rebuilds the list using all the childnodes from the nodes $$anonymous$$onsters
for ( var i : int = 0; i < nodeList.Count; i++ )
{
Debug.Log( nodeList[i].Attributes["name"].Value );
Debug.Log( nodeList[i].Attributes["health"].Value );
Debug.Log( nodeList[i].Attributes["attack"].Value );
Debug.Log( nodeList[i].Attributes["defense"].Value );
}
using the xml file
<$$anonymous$$onsterCollection>
<$$anonymous$$onsters>
<$$anonymous$$onster name="EarthDragon"
health="45"
attack="49"
defense="49">
</$$anonymous$$onster>
<$$anonymous$$onster name="FireDragon"
health="39"
attack="52"
defense="43">
</$$anonymous$$onster>
</$$anonymous$$onsters>
</$$anonymous$$onsterCollection>
so what's the point of using xml over a text file if I have to read the whole thing anyway? Well I suppose sorting by attribute and only checking a specific attribute is better than reading every line.
And how can that wiki entry be of any use to a newcomer using xml when it's open-ended and doesn't work?
Answer by ArkaneX · Dec 17, 2013 at 09:55 AM
I didn't test the code, but the line which gives you error is wrong and should be corrected in the wiki. Variable monsterCollection
should be of type MonsterContainer
instead of MonsterCollection
.
In the wiki sample, author remapped a class MonsterContainer
to a root XML element named MonsterCollection. After deserialization of this XML, you should have an instance of MonsterContainer
class, not MonsterCollection
class (because there is no such class). For me, such remapping might be a source of confusion, and I try to avoid this in most cases, especially if I can control the structure of XML.
If this mixing is no problem for you, just leave it as it is. But I would just change MonsterContainer
class name to MonsterCollection
.
Thanks ArkaneX, that does indeed remove the error. Unfortunately that's where the wiki ends, so I have no idea how to find the desired Attribute and then collect the Nested Elements under that Attribute. This whole process has been very frustrating, and I have a hard time understanding any of the .NET documentation. So I think I'm just going to use a text file. The method in my above comment is basically the same (without knowing how to read subchild elements rather than different attributes), and text requires less formatting.
It would have been great if the author of that wiki actually showed how to filter out and loop through the attributes, then gather the subchild nested elements. Basically people who know how to use xml wouldn't need that wiki, and people like me (with no clue) are left in the dark, totally useless.
Thank you again for taking the time to read through and provide an answer. I shall upvote now, and if no-one (i.e. Bunny) leaves any further information, I shall accept and close this question.
Sorry alucardj - I thought that you know what to do after reading X$$anonymous$$L. That's why my answer was related just to fixing the error you mentioned.
Assu$$anonymous$$g the wiki code works, calling $$anonymous$$onsterContainer.Load
should cause deserialization of the X$$anonymous$$L file content into $$anonymous$$onsterContainer
class instance. This means, that all properties of this class, mapped to elements/attributes in X$$anonymous$$L, will be automatically filled with proper values. For example, variable $$anonymous$$onsters
should be initialized with two monsters you defined in your X$$anonymous$$L. Variable name
of the first monster will be set to EarthDragon
, and the second one to FireDragon
. Variables health
, attack
and defense
will not be filled automatically though, because they don't match any element in the file. And that's because letter casing is important - your variables are lowercase, while first letter in element names is uppercase. To fix this, you can annotate your variables with proper X$$anonymous$$L attributes, e.g.
@XmlElement("Health")
public var health : int;
This way serializer knows that after reading Health element in $$anonymous$$onster element, it should use it to initialize health variable in $$anonymous$$onster class instance. You could of course change name of X$$anonymous$$L element from Health to health as well.
Anyway - now you have your monster collection filled with your dragon data from file. And they're already standard .NET classes, not X$$anonymous$$L data any more, so you can iterate using for
or foreach
, you can use LINQ, etc.
What I just wrote might be a bit chaotic, but if you have any questions, please let me know. I also suggest reading some xml serialization tutorial, for example this. It is for C#, but translating it to UnityScript should be no problem.
Second part of the tutorial I linked is more suitable for your case, but anyway - please read both :)
Thank you for the resources. After 2 hours of more frustration, I havn't made any progress, all the while thinking about how my project is now just sitting there when all was going so well.
I shall definitely continue to try and learn from that tutorial, but in the meantime all I need to continue my project is to read a pre-made X$$anonymous$$L file without all the serialization/deserialization.
Would you $$anonymous$$d having a look at my forum post using another method?
This may seem like I'm after the easy way out, and that's partly correct, but your advice was not in vain, I shall continue to try and fathom the serialization/deserialization method.
Just to prove I'm not full of s***, I got past all the errors in the example code, and importing namespaces, and got up to Attributes.
From the start, there was something strange going on :
<?xml version="1.0" encoding="utf-8"?>
<SomethingIsWrongHere xmlns:xsi="http://www.w3.org/2001/X$$anonymous$$LSchema-instance" xmlns:xsd="http://www.w3.org/2001/X$$anonymous$$LSchema" Number="4">
<Street>Rohini</Street>
<CityName>Delhi</CityName>
</SomethingIsWrongHere>
wtf?!
I couldn't create a list of addresses, I don't know what class/namespace List is supposed to inherit from, but it wouldn't even compile :
Assets/_Scripts/xmlReader/XmlTutorial_Write.js(54,18): BCE0017: The best overload for the method 'XmlTutorial_Write.Serialize(AddressDetails)' is not compatible with the argument list '(System.Collections.Generic.List.<AddressDetails>)'.
m'kay...
#pragma strict
import System.Xml.Serialization; // for XmlSerializer
import System.IO; // for TextWriter/StreamWriter
//import System.Collections.Generic; // to use List ** NOPE !
// Assets/_Scripts/xmlReader/XmlTutorial_Write.js(54,18):
// BCE0017: The best overload for the method 'XmlTutorial_Write.Serialize(AddressDetails)'
// is not compatible with the argument list '(System.Collections.Generic.List.<AddressDetails>)'.
// simple class that needs to be serialized
@XmlRoot( "SomethingIsWrongHere" )
public class AddressDetails
{
@XmlAttribute( "Number" )
public var HouseNo : int;
@XmlElement( "Street" )
public var StreetName : String;
@XmlElement( "CityName" )
public var City : String;
private var PoAddress : String;
}
// Code to serialize the above class
//public static function $$anonymous$$ain( args : String[] )
public function DoSomething()
{
/*
// Part 2
var AddressList : List.< AddressDetails > = new List.< AddressDetails >();
// address 1
var detail1 : AddressDetails = new AddressDetails();
detail1.HouseNo = 4;
detail1.StreetName = "Rohini";
detail1.City = "Delhi";
// address 2
var detail2 : AddressDetails = new AddressDetails();
detail2.HouseNo = 55;
detail2.StreetName = "Perplexed";
detail2.City = "Confused";
AddressList.Add( detail1 );
AddressList.Add( detail2 );
Serialize( AddressList );
*/
/*
// Part 1
var details : AddressDetails = new AddressDetails();
details.HouseNo = 4;
details.StreetName = "Rohini";
details.City = "Delhi";
Serialize( details );
*/
}
public static function Serialize( details : AddressDetails )
{
var serializer : XmlSerializer = new XmlSerializer( AddressDetails );
//var writer : TextWriter = new StreamWriter( Application.dataPath + "/$$anonymous$$yXml.xml" );
var writer : StreamWriter = new StreamWriter( Application.dataPath + "/$$anonymous$$yXml_Write.xml" );
serializer.Serialize( writer, details );
}
// Lets Run It
function Start()
{
DoSomething();
}
I think I learned a little reading the rest, so I shall rewrite my monster.xml and try reading (pt2)
Your answer
Follow this Question
Related Questions
"Root Element Missing: XML Exception" when trying to load data from XML file 2 Answers
XML Serialization issues 0 Answers
loading XML quiz 1 Answer
XML Serialization not working on WebGL 0 Answers
Serialize a class that extends as a serializable class? 0 Answers