0

I am populating a few elements like a ScrollView with data from a few XML files. By clicking a button, the most recent files are being fetched from a server. I am trying to display them directly in the GUI, but I always have to restart the application in order to see the last downloaded version. Downloading and writing works properly.

Here's some code:

First the downloading/updating process

public bool update() {
    try {
        // Download + Save
        WebClient wc = new WebClient();
        wc.Credentials = new NetworkCredential("anonymous", "");
        wc.DownloadFile(_address_employeesXML, _filePathToXMLImport + "/employees.xml");

        XMLReader test = new XMLReader(logController);
        test.readXMLFiles(); 
        Debug.Log("Update successful");
    } catch (Exception e) {
        #if UNITY_EDITOR
        EditorUtility.DisplayDialog("Error",  e.Message, "OK");
        #endif
        XMLReader test = new XMLReader(logController);
        //test.readXMLFiles();
        Debug.LogException(e);
        return false;
    }
    return true;
}

then the read process:

    public bool readEmployees()
{
    string pathToEmployeeXML = _filePathToXMLImport + "/employees.xml";
    if (File.Exists(pathToEmployeeXML))
    {
        using (XmlReader reader = XmlReader.Create(pathToEmployeeXML))
        {
            while (reader.Read())
            {
                // Only detect start elements.
                if (reader.IsStartElement())
                {
                    switch(reader.Name)
                    {
                        case "employee":

                            XmlReader inner = reader.ReadSubtree();

                            inner.ReadToDescendant("firstName");
                            string firstName = inner.ReadElementString();
                            inner.ReadToDescendant("lastName");
                            string lastName = inner.ReadElementString();

                            generateEmployee(firstName, lastName);
                            break;
                    }
                }
            }
        }
        return true;
    }
    return false;
}

I found this thread: Reloading XML asset in Unity Which unfortunately is not really solving my problem.

I also tried using AssetDatabase.Refresh, but didn't really find out how to implement it to be honest.

EDIT:

So now I changed it up to use File.IO and to load the files async. I still need to restart the app to see the changes though.

public class XMLController : MonoBehaviour {

public MessageLogsController logController;
public ApplicationStateSaver saver;

const string _address = "ftp://127.0.0.1:2121/";
const string _address_maintenancePlansXML = _address + "maintenancePlans.xml";
const string _address_employeesXML = _address + "employees.xml";
const string _address_materialsXML = _address + "materials.xml";
const string _address_machinesXML = _address + "machines.xml";
const string maintenancePlans = "maintenancePlans";
const string employees = "employees";
const string materials = "materials";
const string machines = "machines";
// Use this for initialization
void Start () {
    XMLReader XMLReader = new XMLReader(logController);

    StartCoroutine(GetXML(_address_maintenancePlansXML, maintenancePlans));
    StartCoroutine(GetXML(_address_employeesXML, employees));
    StartCoroutine(GetXML(_address_materialsXML, materials));
    StartCoroutine(GetXML(_address_machinesXML, machines));

    XMLReader.readXMLFiles();
    saver.Initialize();
}

public void WriteInformationToXML()
{
    XMLWriter writer = new XMLWriter();
    writer.writeXML();

}

IEnumerator GetXML(string link, string file_name) {
    UnityWebRequest www = UnityWebRequest.Get(link);

    yield return www.SendWebRequest();

    Debug.Log("Success");

    if(www.isNetworkError || www.isHttpError) {
        Debug.Log(www.error);
    }
    else {
        // Show results as text
        Debug.Log(www.downloadHandler.text);

        string savePath = string.Format("{0}/{1}.xml", Application.persistentDataPath + "/Files/XML/Import/", file_name);        
        System.IO.File.WriteAllText(savePath, www.downloadHandler.text);
        Debug.Log("Files written");

    }
}
nicenoize
  • 99
  • 4
  • Can you post some code? What did you try? How can we solve your problem without more informations? – Ludovic Feltz Jul 19 '19 at 12:36
  • I added some code. I though I'm just missing out on an existing function, which helps reloading a canvas/certain assets without restarting the application. – nicenoize Jul 19 '19 at 13:15

2 Answers2

1

I would rather make the whole process async in order to not block the mainthread using UnityWebRequest for the download.

Actually you don't really have to write to the file and read it in again ... you could directly use the downloaded string and pass it to an XmlSerializer (you have to implement a class representing the xml layout - see Saving and Loading Data: XmlSerializer).

If you still need/want to write I would write to Application.persitentDataPath and read/write the file using a FileStream

public void update(Action<bool> success)  
{
    StartCoroutine(UpdateRoutine(onSuccess)); 
}

private string authenticate(string username, string password) 
{
    string auth = username + ":" + password;
    auth = System.Convert.ToBase64String(System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes(auth));
    auth = "Basic " + auth;
    return auth; 
}  

private IEnumerator UpdateRoutine (Action<bool> onSuccess) 
{   
    using(var www = UnityWebRequest.Get(_address_employeesXML)
    {
        string authorization = authenticate("YourUserName", "YourPassWord");
        www.SetRequestHeader("AUTHORIZATION", authorization);

        yield return www.SendRequest();

        if(www.error)
        {
             success?.Invoke(false);
        } 
        else
        {
            // you could ofcourse simply use the returned filecontent now like
            var xml = www.downloadHandler.text;

            var xmlSerializer = new XmlSerializer(typeof(YOURDATA_CLASS));

            var dataClass = (YOURDATA_CLASS) xmlSerializer.Deserialize(xml);
            success?.Invoke(true);


            // However if you prefer to write the file 
            WriteFile(xml, success);
        }
    }
}

// Usually async void is bad
// but with a callback its fine
private async void WriteFile(string xml, Action<bool> success)
{ 
    try
    {
        var data = Encoding.UTF8.GetBytes(xml);

        using(var file = File.Open(Path.Combine(_filePathToXMLImport, "employees.xml", FileMode.OpenOrCreate))
        {
            await file.WriteAsync(data, 0, data.length);

            success?.Invoke();
        }
    }
    catch
    {
        success?.Invoke(false);
    }
}      

then the read process:

public void ReadFile(Action<bool> success)
{
    using(var file = File.Open(Path.Combine(_filePathToXMLImport, "employees.xml", FileMode.Open))
    {
        var data = new byte[(int)file.length];
        await file.ReadAsync(data, 0, data.length);

        var xml = Encoding.UTF8.GetString(Data);

        var xmlSerializer = new XMLSerializer(typeof(YOURDATA_CLASS));

        var dataClass = (YOURDATA_CLASS) xmlSerializer.Deserialize(xml);
        success?.Invoke(true);
    }
}

Note: Typed on smartphone so no warranty but I hope the idea gets clear

derHugo
  • 83,094
  • 9
  • 75
  • 115
  • Thanks for the response. I implemented the async fetching process. I am also writing to Application.persitentDataPath, but unfortunately I still need to restart the App in order to update the Assets. – nicenoize Jul 23 '19 at 14:17
  • your code worked perfectly, I just executed it from the false point. – nicenoize Aug 01 '19 at 11:32
0

The solution to my problem was moving the code I mentioned above to the Controller class that is responsible for the StartView.

nicenoize
  • 99
  • 4