Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Lightweight UnityScript XML parser

Discussion in 'Scripting' started by Flimgoblin, Jan 5, 2010.

  1. ersaurabh101

    ersaurabh101

    Joined:
    Oct 8, 2010
    Posts:
    412
  2. ersaurabh101

    ersaurabh101

    Joined:
    Oct 8, 2010
    Posts:
    412
    @ Flimgoblin

    Sir can u please use my example and make me understand using that senario ??

    Let us start from here -

    i have a prefab called - prefab circle [ a java script named - "script circle" is assigned to it]

    now what code do i type in "script circle" , in assets folder i have created a file named item.xml, what code do i write there ?? [x,y,z,rotation,scale to read from xml]

    hoping for a solution
     
  3. zephyr1125

    zephyr1125

    Joined:
    Apr 8, 2011
    Posts:
    1
    I tried to parse a xml file. the xml file have 20 "status" nodes.

    Code (csharp):
    1.  
    2.         var firstTweet : String;
    3. //          firstTweet = firstTweet.Concat(dataNode.GetValue("statuses>0>status>0>created_at>0>_text"));
    4. //          firstTweet = firstTweet.Concat(dataNode.GetValue("statuses>0>status>1>created_at>0>_text"));
    5.             firstTweet = firstTweet.Concat(dataNode["statuses"][0]["status"].Count);
    6.  
    1.I try to get text from "created_at"[0] of "status"[0], it works fine, i received the correct string.

    2.I tried to get text from "created_at"[0] of "status"[1]. it returns error:
    Code (csharp):
    1.  
    2. ArgumentOutOfRangeException: Index is less than 0 or more than or equal to the list count.
    3. Parameter name: index
    4. 1
    5. System.Collections.ArrayList.ThrowNewArgumentOutOfRangeException (string,object,string) <IL 0x00008, 0x00064>
    6. System.Collections.ArrayList.get_Item (int) <IL 0x00023, 0x00083>
    7.  
    3. so I trie to get the count of "status", it returns "1", but there are actually 20 "status" in the xml file.

    the xml file is very long, so i upload it there:
    View attachment $test.rar

    thank you!
     
  4. De-Panther

    De-Panther

    Joined:
    Dec 27, 2009
    Posts:
    589
    Unity3.3 JSON javascript with #pragma strict
    based on your code

    Code (csharp):
    1.  
    2. #pragma strict
    3.  
    4. /*
    5.  * UnityScript JSON Parser
    6.  * by Fraser McCormick (unityscripts@roguishness.com)
    7.  * http://twitter.com/flimgoblin
    8.  * http://www.roguishness.com/unity/
    9.  *
    10.  * You may use this script under the terms of either the MIT License
    11.  * or the Gnu Lesser General Public License (LGPL) Version 3.
    12.  * See:
    13.  * http://www.roguishness.com/unity/lgpl-3.0-standalone.html
    14.  * http://www.roguishness.com/unity/gpl-3.0-standalone.html
    15.  * or
    16.  * http://www.roguishness.com/unity/MIT-license.txt
    17.  */
    18.  
    19. public static function ParseJSON(json):Hashtable{
    20.     var hash=new Hashtable();
    21.     var parents=new Array();
    22.     var array:Array;
    23.     var quote="\""[0];
    24.         var quoted=false;
    25.         var haveValue=false;
    26.         var quotedString="";
    27.         var isKey=true;
    28.         var lastKey="";
    29.         var inArray=false;
    30.         for(var i=1;i<(json as String).length-1;i++){
    31.             var c=(json as String)[i];
    32.             if(c==quote){
    33.                 if(quoted){
    34.                     // end of an element
    35.                     quoted=false;
    36.                     haveValue=true;
    37.                 }else{
    38.                     // start of an element
    39.                     quoted=true;
    40.                     quotedString="";
    41.                 }
    42.             }else{
    43.            
    44.                 if(quoted){
    45.                     quotedString+=c;
    46.                 }else{
    47.                     if(c=="{"[0]){
    48.                         if(inArray){
    49.                             // hash in an array
    50.                             hash=new Hashtable();
    51.                             array[array.length]=hash;
    52.                             parents.Push(array);
    53.                             inArray=false;
    54.                         }else{
    55.                             // open a new hash
    56.                             hash[lastKey]=new Hashtable();
    57.                             parents.Push(hash);
    58.                             hash=hash[lastKey] as Hashtable;
    59.                             lastKey="";
    60.                         }
    61.                     }else if(c=="["[0]){
    62.                         // start of an array
    63.                    
    64.                         if(inArray){
    65.                             // array in an array
    66.                             array[array.length]=new Array();
    67.                             parents.Push(array);
    68.                             array=array[array.length-1] as Array;
    69.                         }else{
    70.                             // array in a hash
    71.                             array=new Array();
    72.                             parents.Push(hash);
    73.                             hash[lastKey]=array;
    74.                             lastKey="";
    75.                         }
    76.                         inArray=true;              
    77.                     }else if(c=="]"[0] || c=="}"[0]){
    78.                         // end of array
    79.                        
    80.                         if(haveValue || quotedString){
    81.                             if(inArray){
    82.                                 array.Push(quotedString);
    83.                             }else{
    84.                                 hash[lastKey]=quotedString;
    85.                                 lastKey="";
    86.                             }
    87.                             haveValue=false;
    88.                             quotedString="";
    89.                         }
    90.  
    91.                         var par=parents.Pop();
    92.                         if(par instanceof Hashtable){
    93.                             hash=par as Hashtable;
    94.                             inArray=false;
    95.                         }else{
    96.                             array=par as Array;
    97.                             inArray=true;
    98.                         }
    99.                        
    100.                     }else if(c==":"[0]){
    101.                         lastKey=quotedString;
    102.                         haveValue=false;
    103.                         quotedString="";
    104.                     }else if(c==","[0]){
    105.                         // end of value
    106.                        
    107.                         if(haveValue || quotedString){
    108.                             if(inArray){
    109.                                 array.Push(quotedString);
    110.                             }else{
    111.                                 hash[lastKey]=quotedString;
    112.                                 lastKey="";
    113.                             }
    114.                             haveValue=false;
    115.                             quotedString="";
    116.                         }
    117.                     }else{
    118.                         quotedString+=c;
    119.                     }
    120.                 }
    121.             }
    122.         }
    123.         if(haveValue || quotedString){
    124.             if(inArray){
    125.                 array.Push(quotedString);
    126.             }else{
    127.                 hash[lastKey]=quotedString;
    128.                 lastKey="";
    129.             }
    130.             haveValue=false;
    131.         }
    132.    
    133.     return hash;
    134. }
    135.  
    example:
    Code (csharp):
    1.  
    2. var hash=JSON.ParseJSON("{ \"test\":\"thing\", \"an_array\":[ \"one\",\"two\",\"three\"]}");
    3. print((hash["an_array"] as Array)[0]);
    4.  
     
  5. Dangerous Dave

    Dangerous Dave

    Joined:
    May 6, 2011
    Posts:
    1
    This is an awesome piece of code.

    With an XML file like the one below that has multiple nodes with the same name, but different attribute values, can I somehow choose the proper node using the attribute value or is there some other way to access the proper node? For example, can I somehow specify <dialogue id="1"> as the node I'm accessing? Sorry if this is a bit unclear.
    Code (csharp):
    1.  
    2. <?xml version="1.0"?>
    3. <dialogues>
    4.    <dialogue id="0">
    5.       <page id="0">
    6.           <prompt>"Hello there, stranger!"</prompt>
    7.          <options>
    8.             <option id="0">
    9.                 <text> "Who are you?" </text>
    10.                 <link> 1 </link>
    11.                 <reputation>0</reputation>
    12.             </option>
    13.             <option id="1">
    14.                 <text> "Never Mind." </text>
    15.                 <link> 99 </link>
    16.                 <reputation>0</reputation>
    17.             </option>
    18.            </options>
    19.       </page>
    20.       <page id="1">
    21.           <prompt>"John Wettlocke's the name! You can call me Jim for short."</prompt>
    22.          <options>
    23.             <option id="0">
    24.                 <text> "That's a dumb name" </text>
    25.                 <link> 99 </link>
    26.                 <reputation>0</reputation>
    27.             </option>
    28.             <option id="1">
    29.                 <text> "That's a nice name." </text>
    30.                 <link> 99 </link>
    31.                 <reputation>0</reputation>
    32.             </option>
    33.            </options>
    34.       </page>
    35.    </dialogue>
    36. </dialogues>
     
  6. Flimgoblin

    Flimgoblin

    Joined:
    Nov 25, 2009
    Posts:
    89
    Very strange, shall try and track that down. Sorry for the late reply - need more time in the day :)
     
  7. Flimgoblin

    Flimgoblin

    Joined:
    Nov 25, 2009
    Posts:
    89
    There's not really any searching in there, but that's a good feature to add. If I can find some time to spend on this thing I'll put that in :)
     
  8. MaDDoX

    MaDDoX

    Joined:
    Nov 10, 2009
    Posts:
    764
    A little tester I've converted for it:

    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class xmlSimpleTest : MonoBehaviour
    5. {
    6.     void Start()
    7.     {
    8.         string xmlString = "<example><value type=\"String\">Foobar</value><value type=\"Int\">3</value></example>";
    9.  
    10.         XMLReader parser = new XMLReader();
    11.         XMLNode node = parser.read(xmlString);
    12.         printXML(node, 0);
    13.  
    14.     }
    15.  
    16.     void printXML( XMLNode node, int indent) {
    17.         indent++;
    18.  
    19.         foreach (XMLNode n in node.children)
    20.         {
    21.             string attr = " ";
    22.             foreach (var p in n.attributes)
    23.             {
    24.                 attr += "[" + p.Key + ": " + p.Value + "] ";
    25.             }
    26.             Debug.Log(new string('0', indent-1) + " " + n.tagName + attr);
    27.             printXML(n, indent);
    28.          
    29.         }
    30.     }
    31. }
     
    Last edited: Jul 1, 2011
  9. Ashkan_gc

    Ashkan_gc

    Joined:
    Aug 12, 2009
    Posts:
    1,117
    is there any way to use the script in C#?
    did anyone ported the code to C#?
    i did not try using js arrays in c# however they seem to be normal .NET classes.
     
  10. A_cube

    A_cube

    Joined:
    Jul 21, 2011
    Posts:
    3
    someone has an project example ?
     
  11. SolleXelloS

    SolleXelloS

    Joined:
    Aug 30, 2010
    Posts:
    17
    I haven't tested this out but this is a conversation of the code to C#,
    I will be testing it out this week
     

    Attached Files:

  12. Somian

    Somian

    Joined:
    Nov 22, 2011
    Posts:
    38
    Hi, how can I read a value from the hash? When i parse the example file like that…

    Code (csharp):
    1.     var node:XMLNode=parser.Parse(levelList.text);
    2.     var hash:XMLNode=node.GetNode("example>0>value>1");
    3.     print(hash["value"]);
    4.  
    the output is "Null". How can I get the "3"?
     
    Last edited: Dec 1, 2011
  13. Flimgoblin

    Flimgoblin

    Joined:
    Nov 25, 2009
    Posts:
    89
    What's the example XML that you're parsing?
     
  14. Somian

    Somian

    Joined:
    Nov 22, 2011
    Posts:
    38
  15. Flimgoblin

    Flimgoblin

    Joined:
    Nov 25, 2009
    Posts:
    89
    You need to do:

    rather than

     
  16. Somian

    Somian

    Joined:
    Nov 22, 2011
    Posts:
    38
    thanks.
    Code (csharp):
    1.  
    2.  
    i got another problem: I have an xml like

    Code (csharp):
    1. <levellist>
    2.     <levelpack name="levelPack1" orbsrequired=0>
    3.         <level name=”level1“ goalscore=5 timelimit=10.0>1</level>
    4.         <level name=”level2“ goalscore=5 timelimit=10.0>2</level>
    5.         <level name=”level3“ goalscore=5 timelimit=10.0>3</level>
    6. []
    and i'm trying to get a level name via

    Code (csharp):
    1. var levelName:String=node.GetValue("levellist>0>levelpack>0>level>0>@name");
    2.  
    but all I get is an empty string o_º

    EDIT: LOLZ, i think I edited the xml with the wrong app. it replaced the "-s with ”-s… could make be crazy because it's like half a pixel difference. only noticed via the syntax highlighting in dashcode :D
     
    Last edited: Dec 20, 2011
  17. monark

    monark

    Joined:
    May 2, 2008
    Posts:
    1,598
    Trying to compile this from the 3.5 beta flash exporter and getting the following errors

    Anyone got any idea if these are fixable?
     
  18. monark

    monark

    Joined:
    May 2, 2008
    Posts:
    1,598
    Seems the C# version does compile, haven't tried using it yet but it does compile at least.
     
  19. monark

    monark

    Joined:
    May 2, 2008
    Posts:
    1,598
    So i've now tried this in flash and I'm not getting xmlNodeList returning anything but null.

    From the debugger here is where the problem lies in the .as file. This all works fine in the original unity js file, below is a snippet from the compiled .as where the null is generated.

    Code (csharp):
    1.  
    2. var $nodeList: XMLNodeList = $xMLNode.XMLNode_GetNodeList_String("database>0>models>0>component");
    3. var $enumerator: IEnumerator = UnityRuntimeServices.UnityRuntimeServices_GetEnumerator_Object($nodeList);
    4.  
     
  20. monark

    monark

    Joined:
    May 2, 2008
    Posts:
    1,598
    found one issue, chars are treated as int char codes, so building a string myString += myChar gives a number. Fixed that with myString += myChar.ToString()

    This happens in XMLParser.cs

    e.g

    textValue += c;

    change to

    textValue += c.ToString();

    Changing all those instances seems to fix it.
     
    Last edited: Jan 12, 2012
  21. Dover8

    Dover8

    Joined:
    Aug 20, 2010
    Posts:
    94
    Hi, I've been using this parser and it has been great so far, though I have just come across a bit of XML that has stumped it. It doesn't seem to handle completely empty attributes like this: <docs/>

    I know that it handles (or at least ignores) the /> end as the BBC Sport Rss xml works fine and it contains attributes like this:
    <atom:link href="http://feeds.bbci.co.uk/sport/0/rss.xml" rel="self" type="application/rss+xml"/> perhaps the parser expects "" within an attribute?

    Any help on this matter would be greatly appreciated.
     
  22. superclint

    superclint

    Joined:
    Mar 28, 2012
    Posts:
    1
    READ ME!
    For anyone who finds this thread, here's the latest (to save you trawling):

    Nissen provides a solid JS update to Filmgoblin's XML Parser - it's well factored, it fixes the bug mentioned by Dover8 above (not handling self terminating nodes) and it maintains XML structure order. (Note: it uses a fairly different structure for nodeNames and attributes etc. to how the original XML Parser works and does not store text values in nodes, eg. <node>Clever text</node> does not store "Clever text" anywhere.)

    If you're wanting to use Filmgoblin's original XML Parser, a useful fix for the empty node problem is provided by abacus. This enables support for self terminating nodes such as <node/>.

    gregorypierce provides a great port of the XML Parser for C# that enables XPath-style support for traversing your XML.

    Unless you have byzantine requirements for your XML parsing, these libs are tiny, fast and do the job extremely well. We use this extensively at Roar Engine for handling complex XML server output and it handles everything we throw at it.
     
    Last edited: Mar 28, 2012
  23. youjustthebest

    youjustthebest

    Joined:
    Apr 1, 2012
    Posts:
    1
    hello,I very want use your lib,but i have some questions,please answer.
    1.read XML file , how can I modify this XML dom tree, then transform dom tree to string,and then write to XML file,give me some sample code,is it support modify xml tree.
    2.use this ,is it run ios,windows,iphone, ipad,??it is important.thanks!
     
  24. cathode26

    cathode26

    Joined:
    Jul 21, 2011
    Posts:
    27
    Using your json parser I get an error on:

    var par=parents.Pop();

    ArgumentOutOfRangeException: Index is less than 0 or more than or equal to the list count.

    The parents stack is empty but it is still trying to pop. I put my json through this validator and it says it is fine.
    http://jsonformatter.curiousconcept.com/


    After looking more closely I have found out that this happened because there can't be any blank lines or spaces before or after the final brace or before the first brace. Also pay special attention to the way the end is formatted.
    "}]}"

    If the brace, and bracket are not together with no spaces there will be a new line in the array which will cause issues.



    Code (csharp):
    1. {
    2.  
    3.     "Sound":[
    4.         {
    5.             "id":"MOUSE_OVER_BUTTON_SOUND",
    6.             "clipname":"Sounds/mouseoverbutton",
    7.             "copies":"1",
    8.             "interrupt":"true",
    9.             "type":"SOUND_CLIP"},
    10.         {
    11.             "id":"TILE_CLEAR_SOUND",
    12.             "clipname":"Sounds/tileclear",
    13.             "copies":"1",
    14.             "interrupt":"false",
    15.             "type":"SOUND_CLIP"},
    16.         {
    17.             "id":"INTRO_SOUND",
    18.             "clipname":"Sounds/intro",
    19.             "copies":"1",
    20.             "interrupt":"false",
    21.             "type":"MUSIC_CLIP"}]}

    I could not figure out a way to cast the Array type in UnityScript to C# so I changed all of the Array type to ArrayList.


    Code (csharp):
    1.  
    2.  
    3.  /*
    4.  * UnityScript JSON Parser
    5.  * by Fraser McCormick (unityscripts@roguishness.com)
    6.  * http://twitter.com/flimgoblin
    7.  * http://www.roguishness.com/unity/
    8.  *
    9.  * You may use this script under the terms of either the MIT License
    10.  * or the Gnu Lesser General Public License (LGPL) Version 3.
    11.  * See:
    12.  * http://www.roguishness.com/unity/lgpl-3.0-standalone.html
    13.  * http://www.roguishness.com/unity/gpl-3.0-standalone.html
    14.  * or
    15.  * http://www.roguishness.com/unity/MIT-license.txt
    16.  */
    17.  
    18. public static function ParseJSON(json):Hashtable{
    19.     var hash=new Hashtable();
    20.     var parents=new ArrayList ();
    21.     var array:ArrayList;
    22.     var quote="\""[0];
    23.         var quoted=false;
    24.         var haveValue=false;
    25.         var quotedString="";
    26.         var isKey=true;
    27.         var lastKey="";
    28.         var inArray=false;
    29.         for(var i=1;i<json.length-1;i++){
    30.    
    31.             var c=json[i];
    32.            
    33.             if(c==quote){
    34.                 if(quoted){
    35.                     // end of an element
    36.                     quoted=false;
    37.                     haveValue=true;
    38.                 }else{
    39.                     // start of an element
    40.                     quoted=true;
    41.                     quotedString="";
    42.                 }
    43.             }else{
    44.            
    45.                 if(quoted){
    46.                     quotedString+=c;
    47.                 }else{
    48.                     if(c=="{"[0]){
    49.                         if(inArray){
    50.                             // hash in an array
    51.                             hash=new Hashtable();
    52.                             array.Add(hash);
    53.                             parents.Add(array);
    54.                             inArray=false;
    55.                         }else{
    56.                             // open a new hash
    57.                             hash[lastKey]=new Hashtable();
    58.                             parents.Add(hash);
    59.                             hash=hash[lastKey];
    60.                             lastKey="";
    61.                         }
    62.                     }else if(c=="["[0]){
    63.                         // start of an array
    64.                    
    65.                         if(inArray){
    66.                             // array in an array
    67.                             array[array.Count]=new ArrayList();
    68.                             parents.Add(array);
    69.                             array=array[array.Count-1];
    70.                         }else{
    71.                             // array in a hash
    72.                             array=new ArrayList();
    73.                             parents.Add(hash);
    74.                             hash[lastKey]=array;
    75.                             lastKey="";
    76.                         }
    77.                         inArray=true;              
    78.                     }else if(c=="]"[0] || c=="}"[0]){
    79.                         // end of array
    80.                        
    81.                         if(haveValue || quotedString){
    82.                             if(inArray){
    83.                                 array.Add(quotedString);
    84.                             }else{
    85.                                 hash[lastKey]=quotedString;
    86.                                 lastKey="";
    87.                             }
    88.                             haveValue=false;
    89.                             quotedString="";
    90.                         }
    91.  
    92.                         var par=parents[parents.Count-1];
    93.                         parents.RemoveAt(parents.Count-1);
    94.                         if(par instanceof Hashtable){
    95.                             hash=par;
    96.                             inArray=false;
    97.                         }else{
    98.                             array=par;
    99.                             inArray=true;
    100.                         }
    101.                        
    102.                     }else if(c==":"[0]){
    103.                         lastKey=quotedString;
    104.                         haveValue=false;
    105.                         quotedString="";
    106.                     }else if(c==","[0]){
    107.                         // end of value
    108.                        
    109.                         if(haveValue || quotedString){
    110.                             if(inArray){
    111.                                 array.Add(quotedString);
    112.                             }else{
    113.                                 hash[lastKey]=quotedString;
    114.                                 lastKey="";
    115.                             }
    116.                             haveValue=false;
    117.                             quotedString="";
    118.                         }
    119.                     }else{
    120.                         quotedString+=c;
    121.                     }
    122.                 }
    123.             }
    124.         }
    125.         if(haveValue || quotedString){
    126.             if(inArray){
    127.                 array.Add(quotedString);
    128.             }else{
    129.                 hash[lastKey]=quotedString;
    130.                 lastKey="";
    131.             }
    132.             haveValue=false;
    133.         }
    134.    
    135.     return hash;
    136. }
    137.  

    Here is some example code on how to access the javascript from C#

    Code (csharp):
    1.  
    2. private Dictionary<string, CAudioClipData> ProcessSounds (string data)
    3. {
    4.     Hashtable jsonSounds = JSON.ParseJSON (data);
    5.     CAudioClipData audioData;
    6.        
    7.     ArrayList sounds = jsonSounds["Sound"] as ArrayList;
    8.        
    9.        
    10.     for (int i = 0; i < sounds.Count; i++)
    11.     {
    12.         Hashtable table = sounds[i] as Hashtable;
    13.            
    14.         AudioClip clip = Resources.Load(table["clipname"].ToString ()) as AudioClip;
    15.         audioData = new CAudioClipData(clip,
    16.                                     Convert.ToInt16 (table["copies"].ToString ()),
    17.                                     Convert.ToBoolean (table["interrupt"].ToString ()),
    18.                                 table["type"].ToString());
    19.            
    20.         string id = table["id"].ToString();
    21.         m_SoundList.Add(id, audioData);
    22.     }
    23.        
    24.     return m_SoundList;
    25. }
    26.  
     
    Last edited: Jul 8, 2012
  25. DougMcFarlane

    DougMcFarlane

    Joined:
    Apr 25, 2009
    Posts:
    197
    I like the way Nissen's code functioned, but I really needed the missing 'text' values.
    ie:<node>Text Value</node>

    • I updated the code to add this feature.
    • I updated the 'children' to a Generic List. (was 'Array')
    • I updated the 'attributes' to a Generic Dictionary. (was 'Boo.Lang.Hash')
    • It works with the '#pragma strict' enabled.

    I tested it on a 'Tiled' Level Editor file, and it seems to work.
    (Not 100% sure I used the correct indices for the textValue Substring!)

    Code (csharp):
    1. #pragma strict
    2. import System;
    3. import System.Collections.Generic;
    4.  
    5. // http://forum.unity3d.com/threads/38273-Lightweight-UnityScript-XML-parser/
    6.  
    7. class XMLNode {
    8.     var tagName : String;
    9.     var textValue : String="";
    10.     var parentNode : XMLNode;
    11.     var children : List.<XMLNode>;
    12.     var attributes : Dictionary.<String, String>;
    13.  
    14.     function XMLNode() {
    15.         tagName = "NONE";
    16.         textValue = "";
    17.         parentNode = null;
    18.         children = new List.<XMLNode>();
    19.         attributes = new Dictionary.<String, String>();
    20.     }
    21. }
    22.  
    23.  
    24. class XMLReader {
    25.     private var TAG_START       : char = "<"[0];
    26.     private var TAG_END         : char = ">"[0];
    27.     private var SPACE           : char = " "[0];
    28.     private var QUOTE           : char = "\""[0];
    29.     private var SLASH           : char = "/"[0];
    30.     private var EQUALS          : char = "="[0];
    31.     private var BEGIN_QUOTE     : String = "" + EQUALS + QUOTE;
    32.  
    33.     function XMLReader() {
    34.     }
    35.  
    36.     function Read(xml:String) : XMLNode {
    37.         var index               : int = 0;
    38.         var lastIndex           : int = 0;
    39.         var rootNode            : XMLNode = new XMLNode();
    40.         var currentNode         : XMLNode = rootNode;
    41.  
    42.         while (true) {
    43.             index = xml.IndexOf(TAG_START, lastIndex);
    44.             if (index < 0 || index >= xml.Length)   break;
    45.             index++; // skip the tag-char
    46.  
    47.             lastIndex = xml.IndexOf(TAG_END, index);
    48.             if (lastIndex < 0 || lastIndex >= xml.Length) break;
    49.            
    50.             var tagLength : int = lastIndex - index;
    51.             var xmlTag : String = xml.Substring(index, tagLength);
    52.            
    53.             // The tag starts with "</", it is thus an end tag
    54.             if (xmlTag[0] == SLASH) {
    55.                 currentNode = currentNode.parentNode;
    56.                 continue;
    57.             }
    58.  
    59.             var openTag : boolean = true;
    60.  
    61.             // The tag ends in "/>", it is thus a closed tag
    62.             if (xmlTag[tagLength - 1] == SLASH) {
    63.                 xmlTag = xmlTag.Substring(0, tagLength - 1); // cut away the slash
    64.                 openTag = false;
    65.             }
    66.  
    67.             var node : XMLNode = ParseTag(xmlTag);
    68.             node.parentNode = currentNode;
    69.             currentNode.children.Add(node);
    70.  
    71.             if (openTag) {
    72.                 var nextNode : int;
    73.                 nextNode = xml.IndexOf(TAG_START, lastIndex);
    74.                 if (nextNode >= 0  nextNode < xml.Length) {
    75.                     node.textValue = xml.Substring(lastIndex+1, nextNode-lastIndex-1).Trim();
    76.                 }
    77.                 else {
    78.                     node.textValue = "";
    79.                 }
    80.                 currentNode = node;
    81.             }
    82.         }
    83.        
    84.         return rootNode;
    85.     }
    86.  
    87.     function ParseTag(xmlTag:String) : XMLNode {
    88.         var node : XMLNode = new XMLNode();
    89.  
    90.         var nameEnd : int = xmlTag.IndexOf(SPACE, 0);
    91.         if (nameEnd < 0) {
    92.             node.tagName = xmlTag;
    93.             return node;
    94.         }
    95.  
    96.         node.tagName = xmlTag.Substring(0, nameEnd);
    97.  
    98.         var attrString : String = xmlTag.Substring(nameEnd, xmlTag.Length - nameEnd);
    99.         return ParseAttributes(attrString, node);
    100.     }
    101.  
    102.  
    103.     function ParseAttributes(attrString:String, node:XMLNode) : XMLNode {
    104.         var index           :int = 0;
    105.         var attrNameIndex   :int = 0;
    106.         var lastIndex       :int = 0;
    107.  
    108.         while (true) {
    109.             index = attrString.IndexOf(BEGIN_QUOTE, lastIndex);
    110.             if (index < 0 || index > attrString.Length) break;
    111.  
    112.             attrNameIndex = attrString.LastIndexOf(SPACE, index);
    113.             if (attrNameIndex < 0 || attrNameIndex > attrString.Length) break;
    114.             attrNameIndex++; // skip space char
    115.             var attrName : String = attrString.Substring(attrNameIndex, index - attrNameIndex);
    116.  
    117.             index += 2; // skip the equal and quote chars
    118.  
    119.             lastIndex = attrString.IndexOf(QUOTE, index);
    120.             if (lastIndex < 0 || lastIndex > attrString.Length) break;
    121.            
    122.             var tagLength : int = lastIndex - index;
    123.             var attrValue : String = attrString.Substring(index, tagLength);
    124.             node.attributes[attrName] = attrValue;
    125.         };
    126.  
    127.         return node;
    128.     }
    129.    
    130.  
    131.     static function PrintXML(node:XMLNode, indent:int) {
    132.         indent ++;
    133.  
    134.         for (var n:XMLNode in node.children) {
    135.             var attr : String = " ";
    136.  
    137.             attr += "[" + n.textValue + "] ";
    138.             for (var p:KeyValuePair.<String, String> in n.attributes) {
    139.                 attr += "<" + p.Key + ": " + p.Value + "> ";
    140.                 //Debug.Log(attr);
    141.             }
    142.  
    143.             var indentString : String = "";
    144.  
    145.             for (var i:int=0; i<indent; i++) {
    146.                 indentString += "-";
    147.             }
    148.  
    149.             Debug.Log("" + indentString + " " + n.tagName + attr);
    150.             PrintXML(n, indent);
    151.         }
    152.     }  
    153.  
    154. }
    155.  
    Here's the code I attached to the camera to test:
    Code (csharp):
    1. #pragma strict
    2. import XMLNode;
    3. import XMLReader;
    4.  
    5. var xmlLevel : TextAsset;
    6.  
    7. function Start () {
    8.     var reader : XMLReader = new XMLReader();
    9.     var rootNode : XMLNode = reader.Read(xmlLevel.text);
    10.     PrintXML(rootNode, 0);
    11. }
    [Edit] I added a default textValue in the constructor.
    I have just finished this and haven't tested it beyond the 'PrintXML()' command.
    Now I just gotta figure out how to get the data out of the extracted data! :)
    I'm fairly new to JavaScript (in Unity) and C#, and haven't really used Dictionaries and Lists much.
    I don't like how I had to (well, maybe not, I'm new!) add both classes to an 'import' for the test. There is no way to define a namespace in JS? Or could I create a wrapper class that contains both of these classes? Hmm, I'll test this idea tomorrow.

    [Edit 2] See where I added the new code in the 'if (openTag) {' block? Should this have been added to before the 'currentNode.children.Add(node);' command? (ie. should the added node include the text value? or will it matter? or will updating it later (as I have it) update the added node also (referenced)?)
     
    Last edited: Jul 18, 2012
  26. DougMcFarlane

    DougMcFarlane

    Joined:
    Apr 25, 2009
    Posts:
    197
    I had a little fun with the above code and tried to simplify its usage, and add a new feature or two.
    I made some functions static and / or private when I thought it was appropriate.

    I'm just getting back into Unity, and just wanted to get the code to work, but not sure if it is the best technique.
    Any advice for restructuring the code to make it more 'professional'?

    An instance of the XMLReader class now has a 'rootNode' XMLNode.
    This node is the root node of the xml file, and all children are stored under the 'rootNode'.

    I added a few new functions.
    The 'path' is how to specify sub-nodes/children.
    Eg: 'layer/data' where 'layer' is a child of the root, and data is a child node of layer.
    If 'path' is blank, it will use the rootNode.

    OMG, all of this recursion code makes my head hurt!

    Here's the new functions and how to use them:

    GetTextValue(path : String) : String
    - returns the 'Text Value' of the node: <node>Text Value</node>
    Code (csharp):
    1. Debug.Log("Level Data: " + levelData.GetTextValue("layer/data"));
    GetAttribute(path : String, attributeName : String) : String
    - returns the 'Value' of the requested attribute belonging to the node: <node attribute="Value" />
    Code (csharp):
    1. Debug.Log("Encoding: " + levelData.GetAttribute("layer/data", "encoding"));
    GetNode(path : String) : XMLNode
    - returns the node found for the specified 'path'
    Code (csharp):
    1. if (levelData.GetNode("layer/data") != null) {
    Warning: NOT TESTED, just included it in case it does work.

    GetNodeList(path : String) : List.<XMLNode>
    - returns a generic list of type <XMLNode> for all nodes matching the last node in the 'path' string.
    - used when multiple nodes share the same name and you need a collection to iterate through.
    Code (csharp):
    1. var allTileSets : List.<XMLNode>;
    2. allTileSets = levelData.GetNodeList("tileset");
    3. for (var n:XMLNode in allTileSets) {
    4.     Debug.Log(n.tagName);
    5. }
    6.  
    7. // Alternative (should work?)
    8. for (var n:XMLNode in levelData.GetNodeList("tileset")) {
    9.     Debug.Log(n.tagName);
    10. }
    11.  
    Warning: NOT TESTED, just included it in case it does work.

    Note: the above four functions also include overloads that include a XMLNode to start with (instead of the default 'rootNode').
    This is useful if using 'GetNodeList' that returns a collection. You can still use the above functions on the returned collection nodes.

    Example code showing how to use XMLReader:
    Code (csharp):
    1. #pragma strict
    2. import XMLReader;
    3.  
    4. var xmlLevel : TextAsset;
    5.  
    6. function Start () {
    7.     var levelData : XMLReader = new XMLReader();
    8.     levelData.Parse(xmlLevel.text);
    9.     Debug.Log(levelData.ToString());
    10.     Debug.Log("Encoding: " + levelData.GetAttribute("layer/data", "encoding"));
    11.     Debug.Log("Level Data: " + levelData.GetTextValue("layer/data"));
    12. }
    And here's the updated XMLReader:
    Code (csharp):
    1. #pragma strict
    2. import System;
    3. import System.Collections.Generic;
    4.  
    5. // http://forum.unity3d.com/threads/38273-Lightweight-UnityScript-XML-parser/page2
    6.  
    7. class XMLReader {
    8.  
    9.     private static var TAG_START     : char = "<"[0];
    10.     private static var TAG_END         : char = ">"[0];
    11.     private static var SPACE         : char = " "[0];
    12.     private static var QUOTE         : char = "\""[0];
    13.     private static var SLASH         : char = "/"[0];
    14.     private static var EQUALS         : char = "="[0];
    15.     private static var QUESTION        : char = "?"[0];
    16.     private static var NEWLINE        : char = "\n"[0];
    17.     private static var BEGIN_QUOTE     : String = "" + EQUALS + QUOTE;
    18.  
    19.     var rootNode : XMLNode;
    20.  
    21.     function ToString() : String {
    22.         var output : String;
    23.         output = ">XML: " + this.rootNode.tagName + NEWLINE;
    24.         output += ToString(this.rootNode, 1);
    25.         return output;
    26.     }
    27.  
    28.     function ToString(node:XMLNode, indent:int) : String {
    29.         var output : String;
    30.         indent ++;
    31.  
    32.         var indentString : String = "";
    33.         for (var i:int=0; i<indent; i++) {
    34.             indentString += ">";
    35.         }
    36.  
    37.         for (var n:XMLNode in node.children) {
    38.             output += indentString + " " + n.tagName + " [" + n.textValue + "] ";
    39.  
    40.             for (var p:KeyValuePair.<String, String> in n.attributes) {
    41.                 output += "<" + p.Key + ": " + p.Value + "> ";
    42.             }
    43.             output += NEWLINE;
    44.             output += ToString(n, indent);
    45.         }
    46.         return output;
    47.     }  
    48.  
    49.     function Parse(xml:String) {
    50.         var index                 : int = 0;
    51.         var lastIndex             : int = 0;
    52.         var currentNode         : XMLNode;
    53.  
    54.         this.rootNode = new XMLNode();
    55.         currentNode = rootNode;
    56.  
    57.         while (true) {
    58.             index = xml.IndexOf(TAG_START, lastIndex);
    59.             if (index < 0 || index >= xml.Length)   break;
    60.             index++; // skip the tag-char
    61.  
    62.             lastIndex = xml.IndexOf(TAG_END, index);
    63.             if (lastIndex < 0 || lastIndex >= xml.Length) break;
    64.            
    65.             var tagLength : int = lastIndex - index;
    66.             var xmlTag : String = xml.Substring(index, tagLength);
    67.  
    68.             // The tag starts with "<?"
    69.             if (xmlTag[0] == QUESTION) {
    70.                 continue;
    71.             }
    72.  
    73.             // The tag starts with "</", it is thus an end tag
    74.             if (xmlTag[0] == SLASH) {
    75.                 currentNode = currentNode.parentNode;
    76.                 continue;
    77.             }
    78.  
    79.             var openTag : boolean = true;
    80.  
    81.             // The tag ends in "/>", it is thus a closed tag
    82.             if (xmlTag[tagLength - 1] == SLASH) {
    83.                 xmlTag = xmlTag.Substring(0, tagLength - 1); // cut away the slash
    84.                 openTag = false;
    85.             }
    86.  
    87.             var node : XMLNode = ParseTag(xmlTag);
    88.             node.parentNode = currentNode;
    89.  
    90.             if (currentNode.parentNode == null) {
    91.                 this.rootNode = node;
    92.             }
    93.             else {
    94.                 currentNode.children.Add(node);
    95.             }
    96.  
    97.             if (openTag) {
    98.                 var nextNode : int;
    99.                 nextNode = xml.IndexOf(TAG_START, lastIndex);
    100.                 if (nextNode >= 0  nextNode < xml.Length) {
    101.                     node.textValue = xml.Substring(lastIndex+1, nextNode-lastIndex-1).Trim();
    102.                 }
    103.                 else {
    104.                     node.textValue = "";
    105.                 }
    106.                 currentNode = node;
    107.             }
    108.         }
    109.     }
    110.  
    111.     private static function ParseTag(xmlTag:String) : XMLNode {
    112.         var node : XMLNode = new XMLNode();
    113.  
    114.         var nameEnd : int = xmlTag.IndexOf(SPACE, 0);
    115.         if (nameEnd < 0) {
    116.             node.tagName = xmlTag;
    117.             return node;
    118.         }
    119.  
    120.         node.tagName = xmlTag.Substring(0, nameEnd);
    121.  
    122.         var attrString : String = xmlTag.Substring(nameEnd, xmlTag.Length - nameEnd);
    123.         return ParseAttributes(attrString, node);
    124.     }
    125.  
    126.  
    127.     private static function ParseAttributes(attrString:String, node:XMLNode) : XMLNode {
    128.         var index             :int = 0;
    129.         var attrNameIndex     :int = 0;
    130.         var lastIndex         :int = 0;
    131.  
    132.         while (true) {
    133.             index = attrString.IndexOf(BEGIN_QUOTE, lastIndex);
    134.             if (index < 0 || index >= attrString.Length) break;
    135.  
    136.             attrNameIndex = attrString.LastIndexOf(SPACE, index);
    137.             if (attrNameIndex < 0 || attrNameIndex >= attrString.Length) break;
    138.             attrNameIndex++; // skip space char
    139.             var attrName : String = attrString.Substring(attrNameIndex, index - attrNameIndex);
    140.  
    141.             index += 2; // skip the equal and quote chars
    142.  
    143.             lastIndex = attrString.IndexOf(QUOTE, index);
    144.             if (lastIndex < 0 || lastIndex >= attrString.Length) break;
    145.            
    146.             var tagLength : int = lastIndex - index;
    147.             var attrValue : String = attrString.Substring(index, tagLength);
    148.             node.attributes[attrName] = attrValue;
    149.         };
    150.  
    151.         return node;
    152.     }
    153.  
    154.  
    155.     // GetTextValue()
    156.     function GetTextValue(path : String) : String {
    157.         return GetTextValue(this.rootNode, path);
    158.     }
    159.    
    160.     function GetTextValue(node : XMLNode) : String {
    161.         return GetTextValue(node, "");
    162.     }
    163.    
    164.     function GetTextValue(node : XMLNode, path : String) : String {
    165.         if (path.Trim() == "") {
    166.             return node.textValue;
    167.         }
    168.         var nodeName : String = PathGetFirstNode(path);
    169.         for (var n:XMLNode in node.children) {
    170.             if (n.tagName == nodeName) {
    171.                 var index : int;
    172.                 index = path.IndexOf(SLASH);
    173.                 if (index >= 0  index < path.Length) {
    174.                     return GetTextValue(n, path.Substring(index+1));
    175.                 }
    176.                 else {
    177.                     return n.textValue;
    178.                 }
    179.             }
    180.         }
    181.         return "";
    182.     }
    183.  
    184.  
    185.     // GetAttribute()
    186.     function GetAttribute(path : String, attributeName : String) : String {
    187.         return GetAttribute(this.rootNode, path, attributeName);
    188.     }
    189.  
    190.     function GetAttribute(attributeName : String) : String {
    191.         return GetAttribute(this.rootNode, "", attributeName);
    192.     }
    193.  
    194.     function GetAttribute(node : XMLNode, attributeName : String) : String {
    195.         return GetAttribute(node, "", attributeName);
    196.     }
    197.  
    198.     function GetAttribute(node : XMLNode, path : String, attributeName : String) : String {
    199.         if (path.Trim() == "") {
    200.             if (node.attributes.ContainsKey(attributeName)) {
    201.                 return node.attributes[attributeName];
    202.             }
    203.             else {
    204.                 return "";
    205.             }
    206.         }
    207.         var nodeName : String = PathGetFirstNode(path);
    208.         for (var n:XMLNode in node.children) {
    209.             if (n.tagName == nodeName) {
    210.                 var index : int;
    211.                 index = path.IndexOf(SLASH);
    212.                 if (index >= 0  index < path.Length) {
    213.                     return GetAttribute(n, path.Substring(index+1), attributeName);
    214.                 }
    215.                 else {
    216.                     if (n.attributes.ContainsKey(attributeName)) {
    217.                         return n.attributes[attributeName];
    218.                     }
    219.                     else {
    220.                         return "";
    221.                     }
    222.                 }
    223.             }
    224.         }
    225.         return null;
    226.     }
    227.  
    228.  
    229.     // GetNode()
    230.     function GetNode(path : String) : XMLNode {
    231.         return GetNode(this.rootNode, path);
    232.     }
    233.  
    234.     function GetNode(node : XMLNode, path : String) : XMLNode {
    235.         var nodeName : String = PathGetFirstNode(path);
    236.         for (var n:XMLNode in node.children) {
    237.             if (n.tagName == nodeName) {
    238.                 var index : int;
    239.                 index = path.IndexOf(SLASH);
    240.                 if (index >= 0  index < path.Length) {
    241.                     return GetNode(n, path.Substring(0, index+1));
    242.                 }
    243.                 else {
    244.                     return n;
    245.                 }
    246.             }
    247.         }
    248.         return null;
    249.     }
    250.  
    251.  
    252.     // GetNodeList()
    253.     function GetNodeList(path : String) : List.<XMLNode> {
    254.         return GetNodeList(this.rootNode, path);
    255.     }
    256.    
    257.     function GetNodeList(node : XMLNode, path : String) : List.<XMLNode> {
    258.         var nodeList : List.<XMLNode>;
    259.         var nodeName : String = PathGetFirstNode(path);
    260.         var index : int = path.IndexOf(SLASH);
    261.         var isLastNode : boolean = false;
    262.         var isFound : boolean = false;
    263.  
    264.         if (index < 0 || index >= path.Length) isLastNode = true;
    265.         if (isLastNode) {
    266.             nodeList = new List.<XMLNode>();
    267.         }
    268.  
    269.         for (var n:XMLNode in node.children) {
    270.             if (n.tagName == nodeName) {
    271.                 isFound = true;
    272.  
    273.                 if (isLastNode) {
    274.                     nodeList.Add(n);
    275.                 }
    276.                 else {
    277.                     return GetNodeList(n, path.Substring(0, index+1));
    278.                 }
    279.             }
    280.         }
    281.         if (isLastNode  !isFound) {
    282.             return null;
    283.         }
    284.         else {
    285.             return nodeList;
    286.         }
    287.     }
    288.  
    289.    
    290.     private static function PathGetFirstNode(path : String) : String {
    291.         var index : int = path.IndexOf(SLASH);
    292.         if (index >= 0  index < path.Length) {
    293.             return path.Substring(0, index);
    294.         }
    295.         else {
    296.             return path.Trim();
    297.         }
    298.     }
    299.  
    300.  
    301.     class XMLNode {
    302.         var tagName : String;
    303.         var textValue : String="";
    304.         var parentNode : XMLNode;
    305.         var children : List.<XMLNode>;
    306.         var attributes : Dictionary.<String, String>;
    307.    
    308.         function XMLNode() {
    309.             tagName = "NONE";
    310.             textValue = "";
    311.             parentNode = null;
    312.             children = new List.<XMLNode>();
    313.             attributes = new Dictionary.<String, String>();
    314.         }
    315.     }
    316.    
    317. }
    318.  
    [Edit] I added a couple new overloads, for looking up attributes, or text values on the current node.
    I added code to handle when looking up attributes that weren't in the collection, was giving error before.
    This seems to be running fine now, I've managed to use this to created a Tiled Xml level map reader. (Now to get Unity to display the level by creating a dynamic mesh!)
     
    Last edited: Jul 20, 2012
  27. PrisedRabbit

    PrisedRabbit

    Joined:
    Aug 14, 2012
    Posts:
    62
    All works fine, I love you!!!
     
  28. Le-nain

    Le-nain

    Joined:
    Nov 26, 2012
    Posts:
    62

    Just want to state that I love you. <3 Kudos
     
  29. magnusm

    magnusm

    Joined:
    Jul 11, 2013
    Posts:
    2
    Hi there, I am trying to parse an RSS feed and use some data from it. (Thanks to AlexG for part of this code below)

    I am not sure how to access certain parts of the feed. I think it's just a problem of not understanding the syntax for XML and the GetValue() function. Any help would really be appreciated! :)

    Cheers


    Code (csharp):
    1. function Start()
    2. {
    3.     Reader();
    4. }
    5.  
    6. function Reader ()
    7. {
    8.     var feed = new WWW("http://www.reddit.com/r/rss.rss");
    9.     yield feed;
    10.  
    11.     if (feed.error != null)
    12.     {
    13.         Debug.Log('error');
    14.     }
    15.     else
    16.     {
    17.         Debug.Log(feed.data);
    18.         parser = new XMLParser();
    19.         var node = parser.Parse(feed.data);
    20.         //Debug.Log(node["rss"]["channel"][6]["title"]);
    21.         Debug.Log(node.GetValue("rss>channel>6>title"));  //<the line in question
    22.     }
    23. }
    p.s - thanks Flimgoblin for the scripts
     
    Last edited: Dec 17, 2013
  30. tredpro

    tredpro

    Joined:
    Nov 18, 2013
    Posts:
    515
    I am needing to create a news feed to read from a xml file from my website. I've read through this whole forum and I am not sure which parts I need.

    i need this feed to display as

    Image
    Title
    Description
    Image
    Title
    Description
    etc.

    anyone that can help me with this I will greatly appreciate it
     
  31. stevemannar

    stevemannar

    Joined:
    Aug 18, 2014
    Posts:
    1
    try this..

    XmlReader xReader = XmlReader.Create(new StringReader(xmlNode));
    while (xReader.Read())
    {
    //parse your data here
    }

    Full Source...http://net-informations.com/q/faq/xmlreader.html

    Steve
     
  32. Locomalito

    Locomalito

    Joined:
    Apr 4, 2013
    Posts:
    3
    how would i be able to loop through the childnodes of a certain node without knowing the names of the childnodes. I am trying to read an xml that changes attributes so i have to loop through its child and compare attribute names to different strings.
     
  33. ychaudhari

    ychaudhari

    Joined:
    May 21, 2019
    Posts:
    2
    How to parse xml file(3d model) in iOS platform from windows platform form local server in Adhoc network.

    Xml File:

    <?xml version="1.0"?>
    <Heatmap xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <fileData />
    <name>Heatmap</name>
    <path>D:\Local Connection\Local Connection\Server\Assets\Resources\Heatmap.prefab</path>
    </Heatmap>
     
  34. FoxySeta

    FoxySeta

    Joined:
    Jun 7, 2017
    Posts:
    6
    I know this thread is a bit old, but I just tested chirhotec's conversion to C# on Unity 2019.3 with this RSS feed here and it took like 14 seconds. Is that normal?

    EDIT: I also tried @Jehu and @SolleXelloS 's conversions just in case. Same execution times.
     
    Last edited: Apr 29, 2020