Search Unity

How to Read an XML document

Discussion in 'Scripting' started by AlucardJay, Dec 17, 2013.

  1. AlucardJay

    AlucardJay

    Joined:
    May 28, 2012
    Posts:
    328
    This is not a duplicate post, but a followup question to the one I have on UnityAnswers : http://answers.unity3d.com/questions/598425/how-to-read-data-with-saving-and-loading-data-xmls.html

    While the reference links in the answer and comments given by the person who answered are thoughtful, I have tried and have gotten nowhere.

    So in the meantime (as I'm very keen to get this implemented to continue working on my project), I'm trying to use a method from the comment by trothmaster in this post : http://forum.unity3d.com/threads/44441-XML-Reading-a-XML-file-in-Unity-How-to-do-it

    I wish to read an XML file, find a specific key, then read and store the sub-element values under that object.

    This is my idea on how it should work :

    Code (csharp):
    1.  
    2. <MyClass>
    3.     <MySubClass>
    4.         <MyObject key = "KeyText">
    5.             <MySubElement1>someInfo</MySubElement1>
    6.             <MySubElement2>someMoreInfo</MySubElement2>
    7.             <MySubElement3>yetMoreInfo</MySubElement3>
    8.         </MyObject>
    9.         <MyObject key = "AnotherKeyText">
    10.             <MySubElement1>someInfo</MySubElement1>
    11.             <MySubElement2>someMoreInfo</MySubElement2>
    12.             <MySubElement3>yetMoreInfo</MySubElement3>
    13.         </MyObject>
    14.     </MySubClass>
    15. </MyClass>
    16.  
    So this is my actual test XML :

    Code (csharp):
    1.  
    2. <MonsterCollection>
    3.     <Monsters>
    4.         <Monster name="EarthDragon">
    5.             <Health>45</Health>
    6.             <Attack>49</Attack>
    7.             <Defense>49</Defense>
    8.         </Monster>
    9.         <Monster name="FireDragon">
    10.             <Health>39</Health>
    11.             <Attack>52</Attack>
    12.             <Defense>43</Defense>
    13.         </Monster>
    14.     </Monsters>
    15. </MonsterCollection>
    16.  
    And the script I am working with :

    Code (csharp):
    1.  
    2. #pragma strict
    3. import System.Xml; // for XmlDocument
    4.  
    5. function Start()
    6. {
    7.     var doc : XmlDocument = new XmlDocument();
    8.     doc.Load( Application.dataPath + "/monsters.xml" );
    9.    
    10.     var root : XmlNode = doc.DocumentElement;
    11.     Debug.Log( root.Name ); // Here is MyClass (MonsterCollection)
    12.    
    13.     var nodeList : XmlNodeList; // who knows what this does
    14.     nodeList = root.SelectNodes( "Monsters" );  // this filters out MySubClass (sub-class Monsters)
    15.     // if I have other sub-classes, I can filter them by whatever they're called (eg MySubClass2)
    16.    
    17.     nodeList = nodeList[0].ChildNodes;  // this creates a list of all MyObjects within MySubClass
    18.     Debug.Log( nodeList.Count ); // this shows I have 2 objects within MySubClass (called Monster)
    19.    
    20.     // loop through each of MyObjects
    21.     for ( var i : int = 0; i < nodeList.Count; i ++ )
    22.     {
    23.         Debug.Log( nodeList[i].Attributes["name"].Value ); // This Shows MyObject[i] key (name = "foo")
    24.        
    25.         // Now how do I get the value of each MySubElement under MyObject[i] ????
    26.         //Debug.Log( nodeList[i].Attributes.Element["Health"].Value ); // MySubElement1
    27.         //Debug.Log( nodeList[i].Attributes.Element["Attack"].Value ); // MySubElement2
    28.         //Debug.Log( nodeList[i].Attributes.Element["Defense"].Value ); // MySubElement3
    29.     }
    30. }
    31.  
    As stated in the comment, how do I read and store the value in each sub-element within a MyObject that is found by a specified key?
     
  2. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    You tried ChildNodes? Because from your XML, it sounds like "Health" is a subnodes of "Monster".
     
  3. AlucardJay

    AlucardJay

    Joined:
    May 28, 2012
    Posts:
    328
    Thanks for the suggestion, that gets me one step closer. However, all the values return null :

    Code (csharp):
    1.  
    2. Debug.Log( nodeList[i].ChildNodes[0].Value ); // returns null
    3. Debug.Log( nodeList[i].ChildNodes[1].Value ); // returns null
    4. Debug.Log( nodeList[i].ChildNodes[2].Value ); // returns null
    5.  
    Do you know the correct syntax to get the ChildNodes[x] XmlElement name and value?
     
  4. ArkaneX

    ArkaneX

    Joined:
    Jul 21, 2012
    Posts:
    14
    Code (csharp):
    1. // reading
    2. for ( var i : int = 0; i < nodeList.Count; i++ )
    3. {
    4.     var node = nodeList[i];
    5.     Debug.Log(node.Attributes["name"].Value );
    6.  
    7.     Debug.Log(node.SelectSingleNode("Health").InnerText);
    8.     Debug.Log(node.SelectSingleNode("Attack").InnerText);
    9.     Debug.Log(node.SelectSingleNode("Defense").InnerText);
    10. }
    11.  
    12. // writing
    13. var earthDragonNode = monstersNode.SelectSingleNode("Monster[@name='FireDragon']");
    14. earthDragonNode.SelectSingleNode("Health").InnerText = "77";
    15.  
    16. Debug.Log(doc.OuterXml);
    17. doc.Save(pathToFile);
    I will try to post a few snippets related to XML serialization later.

    EDIT: You can use ChildNodes too, but you have to use InnerText instead of Value.
     
    Last edited: Dec 17, 2013
  5. AlucardJay

    AlucardJay

    Joined:
    May 28, 2012
    Posts:
    328
    Thank you for all your help, that has indeed solved the problem on this question.

    I just need to understand what the handles are to move around an xml file and extract the information in various layers. Not long ago I learned HashTables, and I thought this would be just as fun, turned out to be very confusing. Like I said on UA, I will still learn all about xml, I've put too much time in to give up now. Thanks again, you've been a great help.
     
  6. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    Code (csharp):
    1.  
    2. using System.Xml.Serialization;
    3.  
    4. public class MonsterCollection
    5. {
    6.     public Monster[] Monsters;
    7. }
    8.  
    9. public class Monster
    10. {
    11.     [XmlAttribute("name")]
    12.     public string Name;
    13.  
    14.     public int Health;
    15.  
    16.     public int Attack;
    17.  
    18.     public int Defense;
    19. }
    20.  
    Code (csharp):
    1.  
    2. FileStream fs = new FileStream("data.xml", FileMode.Open);
    3. MonsterCollection monsters;
    4. XmlSerializer serial = new XmlSerializer(typeof(MonsterCollection));
    5. monsters = serial.Deserialize(fs);
    6.  
     
  7. jc_lvngstn

    jc_lvngstn

    Joined:
    Jul 19, 2006
    Posts:
    1,508
    This DANGIT!!!!
    I can't for the life of me understand why anyone would use the horribly clunky XMLDoc stuff. Using the XMLSerializer makes it so incredibly easy to read and write .net objects. :)
     
    AndreaMar likes this.
  8. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    At least I found someone else here who agrees with me :)
     
    AndreaMar likes this.
  9. AlucardJay

    AlucardJay

    Joined:
    May 28, 2012
    Posts:
    328
    Thanks, though I should point out I'm coding un uJS. This whole problem started when trying to follow the Unity Wiki tutorial on XmlSerializer : http://answers.unity3d.com/questions/598425/how-to-read-data-with-saving-and-loading-data-xmls.html

    At the end, the read example uses a type/class that doesn't exist, and there are no examples on how to extract the data once the xml has been deserialized.

    If you don't want to read the whole question, here are some highlights :

    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?


    how can that wiki entry be of any use to a newcomer using xml when it's open-ended and doesn't work?

    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.
     
  10. ArkaneX

    ArkaneX

    Joined:
    Jul 21, 2012
    Posts:
    14
    Translation of the code posted by KelsoMRK to uJS is quite easy. One difference might be related to uJS convention used in Unity - variable names are camelcase, so they won't map directly to the elements in sample file. That's why I added a few additional attributes:

    Code (csharp):
    1. class MonsterCollection
    2. {
    3.     @XmlArray("Monsters")
    4.     var monsters : Monster[];
    5. }
    6.  
    7. class Monster
    8. {
    9.     @XmlAttribute("name")
    10.     var name : String;
    11.     @XmlElement("Health")
    12.     var health : int;
    13.     @XmlElement("Attack")
    14.     var attack : int;
    15.     @XmlElement("Defense")
    16.     var defense : int;
    17. }
    Code (csharp):
    1. var fs : FileStream = new FileStream(pathToFile, FileMode.Open);
    2. var s : XmlSerializer = new XmlSerializer(typeof(MonsterCollection));
    3. var collection : MonsterCollection;
    4. collection = s.Deserialize(fs) as MonsterCollection;
    5. fs.Dispose();

    and iterating through monsters:

    Code (csharp):
    1. for (monster in collection.monsters)
    2. {
    3.     Debug.Log(monster.name);
    4.     Debug.Log(monster.health);
    5.     Debug.Log(monster.attack);
    6.     Debug.Log(monster.defense);
    7. }
     
  11. AlucardJay

    AlucardJay

    Joined:
    May 28, 2012
    Posts:
    328
    Thanks for that ArkaneX. The last comment was just a quick one before I walked out the door, and was more about how cranky I still am about the wiki tutorial being so incomplete (rather than the helpful comment that was left byKelsoMRK was in C#). Thanks to everyone who commented here.

    Now that you've fixed the error in my question with the deserializer, am going to start looking at how I can extract data from the collection like the above examples and your comment on my question :

    Anyway - now you have your monster collection filled with your dragon data from file. And they're already standard .NET classes, not XML data any more, so you can iterate using for or foreach, you can use LINQ, etc

    Thank you all for your time and patience dealing with someone who is self-taught and has no experience in xml.
     
  12. Banglemoose

    Banglemoose

    Joined:
    Dec 9, 2014
    Posts:
    9
    This seems like a great way of working with XML, but unless I'm misunderstanding your example this is for creating xml attributes. How can you relate this to reading and using data from files?
     
  13. ArkaneX

    ArkaneX

    Joined:
    Jul 21, 2012
    Posts:
    14
    Please take a look at the second code fragment from my last post. Data that is used to create instance of a MonsterCollection class (through deserialization) is read from file, with help of a FileStream.
     
  14. natmaxex

    natmaxex

    Joined:
    Jul 12, 2012
    Posts:
    68