Search Unity

reading Xml files works on pc but not on Android

Discussion in 'Android' started by cstlmode, Jun 23, 2016.

  1. cstlmode

    cstlmode

    Joined:
    Dec 2, 2014
    Posts:
    88
    as i said in the title , i have been stuck with this for days , i have no idee how i will make this work , please need some help,
    ps : i'm aware that those Xml files are somehow zipped "this is how it works in android',so i'm lookin for a simple way to unzip them and read them , like on pc ,
    here is an example of the code that i have written ,

    Code (JavaScript):
    1. #pragma strict
    2.  
    3. import System.Collections.Generic ;
    4. import System.Xml.Serialization ;
    5. import System.Xml;
    6. import System.IO;
    7.  
    8.  
    9. public var JetpackDB : SavedItems;
    10.   var  RootFolder  : String ;
    11. //var  rootFolder2 = Application.persistentDataPath;
    12.  
    13. public class JetpackXmlManager extends  MonoBehaviour {
    14.  
    15.     function Awake(){
    16.      
    17.  
    18.         if(Application.platform==RuntimePlatform.Android)
    19.                RootFolder = "jar:file://" + Application.dataPath + "!/assets/XmlFiles/JetpacksDB.xml";
    20.      
    21.         else if(Application.platform==RuntimePlatform.IPhonePlayer)
    22.             RootFolder = Application.persistentDataPath +"/Raw/XmlFiles/JetpacksDB.xml";
    23.  
    24.         else
    25.           RootFolder = Application.dataPath + "/StreamingAssets/XmlFiles/JetpacksDB.xml";
    26.          
    27.  
    28. //Debug.Log("RootFolder="+RootFolder);
    29.    
    30.         if(System.IO.File.Exists(RootFolder) ) Open();
    31.  
    32.         else Save();
    33.      
    34.  
    35.      
    36.      
    37.  
    38.     }
    39.  
    40.  
    41.         // save function
    42.        public function Save(){
    43.      
    44.            var MySerializer :XmlSerializer = new XmlSerializer(typeof(SavedItems) );
    45.            var MySaveFile :FileStream = new FileStream(RootFolder,FileMode.Create);
    46.                MySerializer.Serialize(MySaveFile,JetpackDB);
    47.                MySaveFile.Close();
    48.        
    49.  
    50.  
    51.  
    52.  
    53.        }
    54.         // Open function
    55.        public function Open(){
    56.  
    57.  
    58.  
    59.        
    60.            var MySerializer :XmlSerializer = new XmlSerializer(typeof(SavedItems) );
    61.  
    62.            var MySaveFile :FileStream= new FileStream(RootFolder,FileMode.Open);
    63.            JetpackDB=MySerializer.Deserialize(MySaveFile) as SavedItems;
    64.                MySaveFile.Close();
    65.          
    66.  
    67.        }
    68. }
    69.  
    70.     public class  Jetpack    {
    71.  
    72.    //  public var InUse :boolean ;
    73.  
    74.  
    75.      public var PurchaseStat :boolean ;
    76.      public var EUConsumptionBoost :float;
    77.      public var BoostTime :float;
    78.      public var BoostReloadTime : float;
    79.      public var Progress :float;
    80.      public var Progress1 :float;
    81.      public var Progress2 :float;
    82.  
    83.     }
    84.  
    85.  
    86.      public class SavedItems  {
    87.  
    88.           public var InUse :int ;
    89.  
    90.           public var NumberOfAMPs :int ;
    91.           public var AMPTime :int ;
    92.           public var AMPReloadTime :int ;
    93.           public var AMPTimeProgress :float;
    94.           public var AMPReloadTimeProgress  :float;
    95.  
    96.           public var MHSTime :int ;
    97.           public var NumberOfMHS :int ;
    98.           public var MHSTimeProgress  :float;
    99.  
    100.           public var NumberOfMagnets :int ;
    101.           public var MagnetTime :int ;
    102.           public var MagnetReloadTime :int ;
    103.           public var MagnetTimeProgress  :float;
    104.           public var MagnetReloadTimeProgress  :float;
    105.  
    106.  
    107.           public var NumberOfShields :int ;
    108.  
    109.           public var NumberOfNukes:int ;
    110.  
    111.           //public var EnergyUnits:int ;
    112.          // public var Plotonium:int ;
    113.  
    114.  
    115.    
    116.  
    117.      @XmlArray("JetpacksList")
    118.         public  var list : List.<Jetpack> = new List.<Jetpack>();
    119.    
    120.     }
    121.  
    122.  
     
  2. Chris-Trueman

    Chris-Trueman

    Joined:
    Oct 10, 2014
    Posts:
    1,261
    Are these xml files referenced anywhere in your project other than in the code here?

    If they are not in a resources folder they will not be included if there is no reference to them.

    For example I have a list of names in an XML file in the resources folder. When the game loads I grab it from the resources folder using the follow code.
    Code (CSharp):
    1. var xml = new XmlSerializer(typeof(string[]));
    2. TextAsset tAsset = Resources.Load("TombstoneNames") as TextAsset;
    3. using (var stream = new StringReader(tAsset.text))
    4. {
    5.     GameConsts.TombstoneNames = xml.Deserialize(stream) as string[];
    6. }
    This works on all platforms that I have tried, and also seems to be the standard way of doing it.
     
  3. Chris-Trueman

    Chris-Trueman

    Joined:
    Oct 10, 2014
    Posts:
    1,261
    Okay I took another look at your question and it seems like you are saving them on the device. This changes it.

    I don't use xml files to save any game information, too editable, but this should work no matter what files you want to read/write.

    Code (CSharp):
    1. string savePath = Application.persistentDataPath + "xmltosave.xml";
    2. //or if you want a subdirectory
    3. string savePath = Application.persistentDataPath + "/XmlFiles/xmltosave.xml";
    4.  
    5. if (File.Exists(savePath))
    6. {
    7.     //Do what you need to do once you have the file.
    8. }
    9. else
    10. {
    11.     //Create a new file here
    12. }
    This should also work on any platform. It works on android and windows I know for sure.

    I have never needed to have anything special happen between pc and android.

    One last thing Application.dataPath shouldn't be used to store things like saved games as it is not read/write on all platforms(docs state read only as well) . To keep is consistent across platforms use Application.persistentDataPath. That way you don't need to worry about if (Android) or if (iPhone) when loading or saving a game.
     
    Last edited: Jun 23, 2016
  4. cstlmode

    cstlmode

    Joined:
    Dec 2, 2014
    Posts:
    88
    @Chris Trueman hello and thank you for your answers , well the first answer was close to what i'm trying to achieve , i have a game that saves some variables in an Xml ,i put that Xml in one directory only , this one Application.dataPath+"/StreamingAssets/XmlFiles/JetpacksDB.xml" it changes to persistentDataPath when Platfrom is Android or Ios

    i put the Xml file in that directory only cause i don't want it to be overwritten when the user makes an update for the game, i don't know that much about the directory of resources , could you give me more insight about it , is it okey if i put a sensitive file like i mentionned in such a directory ?
    also here is anote from unity scripting ref to help you figure out the problem more

    http://docs.unity3d.com/Manual/StreamingAssets.html
    On Android, the files are contained within a compressed .jar file (which is essentially the same format as standard zip-compressed files). This means that if you do not use Unity’s WWW class to retrieve the file, you need to use additional software to see inside the .jar archive and obtain the file.




    i made an attempt to use the WWW here is the code but it does'nt work , i don't know why , the debog.log does not return the string from the xml file



    Code (JavaScript):
    1.   var linkstream : WWW  = new WWW(RootFolder);
    2.            yield  linkstream;
    3.            if (linkstream.isDone)
    4.      
    5.          
    6.            var MyText :String =linkstream.text;
    7.  
    8.            Debug.Log("linkstream"+MyText);
    9. */
     
  5. Chris-Trueman

    Chris-Trueman

    Joined:
    Oct 10, 2014
    Posts:
    1,261
  6. cstlmode

    cstlmode

    Joined:
    Dec 2, 2014
    Posts:
    88
    thanx a lot @Chris Trueman
    i tried the resources methode ,it worked on the pc but not on the phone , so i concentrated on the WWW , it works now on the pc ,it gives me no errors , but as soon as i trigger Save(), i hit me with this error on the face , i've spent to much trime on this one trying to figure out why ,the weird thing is that when i trigger save() , data get saved on the pc with the error , on the phone it gives me the error too but no data saving
    hope someone can help me figure out where is the problem
    here is the error i get

    ArgumentException: Path is empty
    System.IO.FileStream..ctor (System.String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, Boolean anonymous, FileOptions options) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.IO/FileStream.cs:209)
    System.IO.FileStream..ctor (System.String path, FileMode mode)
    (wrapper remoting-invoke-with-check) System.IO.FileStream:.ctor (string,System.IO.FileMode)
    JetpackXmlManager.Save () (at Assets/Resources/JetpackXmlManager.js:45)
    BoostEUConsumptionProgress.Check () (at Assets/BoostEUConsumptionProgress.js:128)
    UnityEngine.GameObject:SendMessage(String)
    ConfirmPayment:Confirm() (at Assets/ConfirmPayment.js:8)
    UnityEngine.EventSystems.EventSystem:Update()


    Code (JavaScript):
    1.  
    2. public class JetpackXmlManager extends  MonoBehaviour {
    3.  
    4.     function Awake(){
    5.  
    6.         if(Application.platform==RuntimePlatform.Android)
    7.             RootFolder ="jar:file://" + Application.persistentDataPath  + "!/assets/XmlFiles/JetpacksDB.xml";
    8.  
    9.         else
    10.             RootFolder =   Application.dataPath + "/StreamingAssets/XmlFiles/JetpacksDB.xml";
    11.    
    12.  
    13.         Open();
    14.     }
    15.  
    16.         // save function
    17.        public function Save(){
    18.  
    19.            var MySerializer :XmlSerializer = new XmlSerializer(typeof(SavedItems) );
    20.            var MySaveFile :FileStream = new FileStream(RootFolder,FileMode.Create);
    21.                MySerializer.Serialize(MySaveFile,JetpackDB);
    22.                MySaveFile.Close();
    23.  
    24.        }
    25.         // Open function
    26.        public function Open(){
    27.  
    28.  
    29.            var MySerializer :XmlSerializer= new XmlSerializer(typeof(SavedItems));
    30.            var linkstream : WWW  ;
    31.            if(Application.platform==RuntimePlatform.Android)   linkstream = new WWW( "jar:file://"+Application.persistentDataPath +"!/assets/XmlFiles/JetpacksDB");
    32.  
    33.        else    linkstream = new WWW("file:///" + Application.dataPath  + "/StreamingAssets/XmlFiles/JetpacksDB");
    34.    
    35.            yield  linkstream;
    36.            if (linkstream.isDone == true) {
    37.                var MySaveFile  = new StringReader(linkstream.text);
    38.  
    39.                JetpackDB= MySerializer.Deserialize(MySaveFile) as SavedItems;
    40.                MySaveFile.Close();
    41.  
    42.            }
    43.  
    44.  
    45.  
    46.  
    47.        }
    48. }
    49.  
     
    Last edited: Jun 24, 2016
  7. Chris-Trueman

    Chris-Trueman

    Joined:
    Oct 10, 2014
    Posts:
    1,261
    Code (CSharp):
    1. RootFolder ="jar:file://"+ Application.persistentDataPath+"!/assets/XmlFiles/JetpacksDB.xml";
    Doesn't exists. There is nothing in there until you put something in there. That's why its not working on android.

    Application.dataPath is like your asset folder in the editor just in the build. Only some stuff gets included in the dataPath. StreamingAssets and Resources get included so you can have access to them easily.

    Application.persistentDataPath is for saving/storage. It has nothing in it when you first install. Docs tell me its read only, but I think that's a type-o as I have wrote to it on windows and android and they use it in this video in the learn section. This is also not overwritten when the user gets an update(persistent). Its also not guaranteed the user doesn't have access to it, but anything can be reverse engineered, so don't spend too much time worrying about it, we like to do things people tell us we can't do.

    To access anything in StreamingAssets use Application.streamingAssetsPath.
    docs: https://docs.unity3d.com/ScriptReference/Application-streamingAssetsPath.html

    So try this
    Code (CSharp):
    1. RootFolder = Application.streamingAssetsPath +"/XmlFiles/JetpacksDB.xml";
    2. if (RootFolder.Contains("://"))
    3. {
    4.     //Use WWW class here on the url that gets returned(Android and Web builds will return them)
    5. }
    6. else
    7. {
    8.     //Open it normally as its not a jar file or in a web build.
    9. }
    It should work on any platform. Application.dataPath/persistentDataPath/streamingAsssetsPath always returns a proper address on each platform, makes if very convenient in cross platform development.
     
  8. cstlmode

    cstlmode

    Joined:
    Dec 2, 2014
    Posts:
    88
    i tried pretty much everything these days , i finally managed to get the textassets to work on the pc ,on the android not yet , once it worked but only the open methode , i get this error when i trigger save()

    IsolatedStorageException: Could not find a part of the path

    so i was pondering if you can help get over this last obstacle , here is my code now , and thanx a lot for bearing up with me



    Code (JavaScript):
    1.  
    2. public   var MyData :Data ;
    3. public var Health :float ;
    4.  
    5. public  var FileName :String = "Data";
    6. public var  FileExt  :String= ".xml"; // only used to save/create the xml file
    7. var Directory :String ="";
    8.  
    9. public class TestDB extends  MonoBehaviour {
    10.  
    11.     function Start(){
    12.  
    13.         if(Application.platform==RuntimePlatform.WindowsEditor) Directory= Application.dataPath+ "/Resources/"+FileName;
    14.         else if(Application.platform==RuntimePlatform.Android) Directory= Application.streamingAssetsPath+ "/Resources/"+FileName ;
    15.    
    16.      
    17.     }
    18.  
    19.     // save function
    20.     public function Save(){
    21.  
    22.    MyData.SavedHealth =Health;
    23.         var MySerializer :XmlSerializer = new XmlSerializer(typeof(Data) );
    24.    
    25.         var MySaveFile :FileStream = File.Create(Directory+FileExt );
    26.         MySerializer.Serialize(MySaveFile,MyData);
    27.         MySaveFile.Close();
    28.        
    29.     }
    30.         // Open function
    31.     public function Open()
    32.     {  
    33.  
    34.  
    35.         if(File.Exists(Directory+FileExt) )
    36.         {
    37.         var MySerializer :XmlSerializer= new XmlSerializer(typeof(Data));
    38.    
    39.         var MyTextAssets : TextAsset  = Resources.Load.<TextAsset>(FileName) ;
    40.    
    41.         var MySaveFile  = new StringReader(MyTextAssets.text);
    42.         MyData= MySerializer.Deserialize(MySaveFile) as Data;
    43.         MySaveFile.Close();
    44.  
    45.    
    46.         Debug.Log("MyText="+MyTextAssets.text);
    47.         Debug.Log("MyData.SavedHealth="+MyData.SavedHealth);
    48.    
    49.         Health=MyData.SavedHealth;
    50.  
    51.         }
    52.  
    53.  
    54.  
    55.     }
     
  9. cstlmode

    cstlmode

    Joined:
    Dec 2, 2014
    Posts:
    88
    it finally worked after 1 week , woow ,
    Thanks a lot @Chris Trueman for bearing up with me , turned out that my problem was misunderstanding the streamingAssets correctly , this was a great lesson for me
    and here is the code that actually works , it saves data as binary file on both UnityWindowsEditor and Android , in case someone needed it someday

    Code (JavaScript):
    1. import UnityEngine;
    2. import System.Collections;
    3. import System ;
    4. import System.Runtime.Serialization.Formatters.Binary ;
    5. import System.IO;
    6.  
    7.  
    8. public   var MyData :Data ;
    9. public var Health :float ;
    10.  
    11. var FileExt :String =".dat";
    12. var Directory :String ="";
    13.  
    14.  
    15. public  class TestDB extends  MonoBehaviour {
    16.  
    17.  
    18.  
    19.     function Start(){
    20. Directory =Application.persistenDataPath+"/Data";
    21.    
    22.     }
    23.     public  function Save ()
    24.     {
    25.         var BF :BinaryFormatter = new BinaryFormatter();
    26.         var MySaveFile :FileStream =File.Create( Directory+FileExt );
    27.    
    28.  
    29.            MyData.SavedHealth =Health;
    30.      
    31.  
    32.            BF.Serialize(MySaveFile ,MyData) ;
    33.            MySaveFile.Close();
    34.  
    35.  
    36.        }
    37.        public function Open () {
    38.  
    39.            var BF :BinaryFormatter = new BinaryFormatter();
    40.  
    41.            if(File.Exists(Directory+FileExt ) ){
    42.  
    43.                Debug.Log("File Exits");
    44.  
    45.                var linkstream : WWW  ;
    46.  
    47.                linkstream = new WWW("file://"+ Directory+FileExt    );
    48.  
    49.  
    50.  
    51.                while ( ! linkstream.isDone) {}
    52.  
    53.                var MySaveFile: MemoryStream   = new MemoryStream(linkstream.bytes);
    54.          
    55.                MyData  = BF.Deserialize(MySaveFile)as Data ;
    56.                MySaveFile.Close();
    57.                Health =MyData.SavedHealth ;
    58.  
    59.            }
    60.        
    61.        }
    62.    }
    63.    public  class Data
    64.    {
    65.        public var SavedHealth :float ;
    66.  
    67.        }
     
    Last edited: Jun 27, 2016
    nathious9536 likes this.
  10. Chris-Trueman

    Chris-Trueman

    Joined:
    Oct 10, 2014
    Posts:
    1,261
    Glad to know you got it working.

    One more thing to help make it a little more efficient.

    Code (CSharp):
    1. if(Application.platform==RuntimePlatform.WindowsEditor) Directory = Application.dataPath+"/StreamingAssets/Data" ;
    2. else if(Application.platform==RuntimePlatform.Android) Directory = Application.persistentDataPath +"/Data";
    Isn't needed. All you need is this:

    Code (CSharp):
    1. Directory = Application.persistenDataPath + "/Data";
    You don't need to worry about the streaming assets folder for what you are doing. The code is only using streaming assets in the editor so its being used for nothing really.
     
  11. cstlmode

    cstlmode

    Joined:
    Dec 2, 2014
    Posts:
    88
    you're totally right , i've changed it , thanx a lot man :D
     
  12. osunil

    osunil

    Joined:
    Oct 17, 2019
    Posts:
    1
    Hey @cstlmode and @Chris-Trueman.
    I followed the thread because I have similar issue. I made a quiz game and the questions/answers are in xml. It reads fine in unity but does not open at all on android phone.
    I did not set up streaming assets or anything. Just wrote script to input questions in unity and the questions/answers are saved as xml files in the Assest folder.

    Any help would be appreciated.
     
  13. Chris-Trueman

    Chris-Trueman

    Joined:
    Oct 10, 2014
    Posts:
    1,261
    How are you loading and reading the XML file?