- Home /
XML reading working in Editor but not in compiled executable
Platform assembly: C:\Users\Guto\Desktop\test_Data\Managed\System.Core.dll (this message is harmless)
Platform assembly: C:\Users\Guto\Desktop\test_Data\Managed\System.Xml.dll (this message is harmless)
Platform assembly: C:\Users\Guto\Desktop\test_Data\Managed\System.dll (this message is harmless)
IsolatedStorageException: Could not find a part of the path "C:\Users\Guto\Desktop\Assets\DEAD ZONE ANDROID\Languages\arachnaZone_EN.xml".
at System.IO.FileStream..ctor (System.String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, Boolean anonymous, FileOptions options) [0x00000] in <filename unknown>:0
at System.IO.FileStream..ctor (System.String path, FileMode mode, FileAccess access, FileShare share) [0x00000] in <filename unknown>:0
at System.Xml.XmlUrlResolver.GetEntity (System.Uri absoluteUri, System.String role, System.Type ofObjectToReturn) [0x00000] in <filename unknown>:0
at Mono.Xml2.XmlTextReader.GetStreamFromUrl (System.String url, System.String& absoluteUriString) [0x00000] in <filename unknown>:0
at Mono.Xml2.XmlTextReader..ctor (Boolean dummy, System.Xml.XmlResolver resolver, System.String url, XmlNodeType fragType, System.Xml.XmlParserContext context) [0x00000] in <filename unknown>:0
at System.Xml.XmlTextReader..ctor (Boolean dummy, System.Xml.XmlResolver resolver, System.String url, XmlNodeType fragType, System.Xml.XmlParserContext context) [0x00000] in <filename unknown>:0
at System.Xml.XmlReader.Create (System.String url, System.Xml.XmlReaderSettings settings, System.Xml.XmlParserContext context) [0x00000] in <filename unknown>:0
at System.Xml.XmlReader.Create (System.String url, System.Xml.XmlReaderSettings settings) [0x00000] in <filename unknown>:0
at System.Xml.XmlReader.Create (System.String url) [0x00000] in <filename unknown>:0
This is the error I get in the outputLog. Everything works fine in the Editor, but no in the executable. The error occurs in both Windows and Android.
Here are the paths for the .xmls:
And here, if necessary is the code which actually returns the string attached to a individual attribute:
public string[] getTexts (Attributes attribute) {
XmlReader reader = XmlReader.Create( xmlPaths[ (int)_curLanguage ] );
int textQuantity = 0;
string[] texts = null;
while( reader.Read () ) {
if( reader.IsStartElement( attribute.ToString( ) ) ) {
textQuantity = int.Parse( reader.GetAttribute( "quantity") );
texts = new string[textQuantity];
for(int i = 0; i < textQuantity; i++) {
reader.Read();
if(reader.IsStartElement(attribute.ToString() + "Tag")) {
texts[i] = (string)reader.ReadString();
}
}
}
}
return texts;
}
Any help would be very appreciated! Thanks from now!
Answer by whydoidoit · Jun 26, 2012 at 01:01 AM
That's not how you would get hold of the files on the device - in a build those files will not exist. You need to either put them in a Resources folder and read them as a TextAsset using Resources.Load - or you could place them in Streaming Assets and get them using that path on the device (or copy them manually into a location during your build). Your Assets... path isn't going to exist on the build though - so you will need to adjust all of those depending on the approach you take.
Yep. Great point! If I just let the .xml in the Resource folder, they'll stay accessable during the game execution? Because I don't know what type holds and .xml file to get it from Resources.Load(..);
Ok, I could load them using the Strea$$anonymous$$gAssets folder path combined with the file name, now it works in the executable! Thanks!
Unfortunatelly, when I tested it in the mobile, the error persists. The code is the following:
void Awake () {
_language$$anonymous$$anager = this;
DontDestroyOnLoad(this);
string strea$$anonymous$$gAssets = Application.strea$$anonymous$$gAssetsPath;
languageFiles = new string[3];
languageFiles[0] = System.IO.Path.Combine(strea$$anonymous$$gAssets, "arachnaZone_EN.xml");
languageFiles[1] = System.IO.Path.Combine(strea$$anonymous$$gAssets, "arachnaZone_PTBR.xml");
languageFiles[2] = System.IO.Path.Combine(strea$$anonymous$$gAssets, "arachnaZone_SN.xml");
}
In the mobile, I get the following path for the file:
jar:file:///data/app/DeadZone.bunker-1.apk/!/assets/arachnaZone_EN.xml
Sorry to be a while getting back to you. Yes on droid the strea$$anonymous$$g assets is compressed into a JAR file so you can't just open it with System.IO (I forgot that, sorry, it's fine on IOS which I use most of the time ).
You can get it from Resources.Load like this:
var textAsset = Resources.Load("Path", typeof(TextAsset)) as TextAsset; //Rename the file to .txt and no extension here
var myText = textAsset.text;
Answer by carterdawson · Jun 30, 2016 at 05:20 PM
As an adjunct to @whydoidoit's answer:
Read the documentation on streaming assets, and you'll see that you need to use the WWW class to fetch the files if you are using Android. System.IO works fine otherwise.
Note that WWW is asynchronous, so you'll need to use Coroutines to kick off the download and wait until it's done. This can be a little bit of a dragon, since on some platforms (Windows, IOS), this will be a file:// url, and it will complete in one frame on those platforms, so if you don't actually wait until the Coroutine finishes, you can end up with subtle bugs that can be really un-obvious to track down when they happen.
So, the best practice there is probably to use WWW, but that means you'll need to turn your function (and any function that calls it, and any function that calls any of those functions, etc. etc.) into a coroutine. Basically, this means that if you have something like this:
void Start() {
process_file_sysio("file.name.txt")
}
private List<int[]> process_file_sysio(string filename)
{
List<int[]> lai_ret;
System.IO.StreamReader file = new System.IO.StreamReader(filename);
lai_from_file_contents(file, lai_ret);
return lai_ret;
}
You'll need to turn it into this:
IEnumerator Start() {
List<int[]> lai = new List<int[]>();
yield return process_file_sysio("file.name.txt");
}
private IEnumerator process_file_sysio(string filename, List<int[]> lai_ret)
{
System.IO.StreamReader file = new System.IO.StreamReader(filename);
lai_from_letter_contents_str(file, lai_ret);
yield return null;
}
Note that I needed to do two changes. First, I had to make process_file_sysio return an IEnumerable instead of a List. Obviously, I can't return lai_ret from that function anymore, so I moved it into the parameters. Then, I had to do the same thing to Start, which calls the function. Make it return IEnumerable, and change the function call to be a "yield return".
Now, of course, all I've done is to replicate what I had before, but I'm still using System.IO, which is a problem. The whole point is to migrate to WWW. All I need to do that is to create a process_file_www() function, like this:
IEnumerator Start() {
List<int[]> lai = new List<int[]>();
yield return process_file_www("file.name.txt");
}
private IEnumerator process_file_www(string filename, List<int[]> lai_ret)
{
WWW w = new WWW("file://" + filename);
yield return w;
StringReader file = new StringReader(w.text);
lai_from_letter_contents_str(file, lai_ret);
}
The function process_file_www is a drop-in replacement for process_file_sysio now that we've Coroutine-enabled everything, so this method will work regardless of the platform. You could even put the file on the internet, and download it that way, and your application will still work (it will just be a little slower).
The last gotcha: It's easy to forget to change the original function call to "yield return". If you do that, you'll get the strange behavior I refer to earlier, where a single frame's worth of your function gets called, but subsequent frames don't. Watch out for that.
I also found this answer on stackoverflow that has an interesting trick to turn the Coroutine into a callback. It doesn't really buy you anything, but it might be helpful if callbacks are more understandable to you than Coroutines are.