XML reading working in Editor but not in compiled executable

Platform assembly: C:\Users\Guto\Desktop est_Data\Managed\System.Core.dll (this message is harmless)
Platform assembly: C:\Users\Guto\Desktop est_Data\Managed\System.Xml.dll (this message is harmless)
Platform assembly: C:\Users\Guto\Desktop est_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 :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 *= (string)reader.ReadString();*
  •   						}*
    
  •   				}*
    
  •   		}*
    
  •   }*
    
  •   return texts;*
    
  • }*
    Any help would be very appreciated! Thanks from now!

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.

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 process_file_sysio(string filename)
    {
        List 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 lai = new List();
      yield return process_file_sysio("file.name.txt");
    }

    private IEnumerator process_file_sysio(string filename, List 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 lai = new List();
      yield return process_file_www("file.name.txt");
    }

    private IEnumerator process_file_www(string filename, List 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.